Files
Arnaud adb2bd3fc1 feat: galerie d'images projet (upload disque) + dashboard /admin
==== Galerie d'images ====

Backend
  - Migration 003 : table project_images (FK CASCADE sur projects)
  - ImagesController : CRUD complet, deux modes de création
      * URL externe via JSON body
      * Upload disque via multipart/form-data avec :
          - validation MIME RÉELLE via finfo (jpeg/png/webp/gif)
          - taille max 5 Mo, vérifiée explicitement (au-delà du php.ini)
          - vérification UPLOAD_ERR_* avec messages clairs
          - stockage dans public/uploads/projects/:slug/<hash>.<ext>
          - dimensions image extraites via getimagesize (best-effort)
          - row DB avec mime_type / size_bytes / width / height
  - Request : ajout isMultipart(), formField(), file()
  - public/index.php : 3 routes (index public, store/update/destroy admin)

Proxy
  - caddy.conf : /uploads/* ajouté au reverse_proxy comme /api/*
  - vite.config.ts : pareil pour le dev local

Frontend
  - Type ProjectImage (api.ts) avec url + storage_path + dimensions
  - uploadFile() helper dans client.ts (FormData + Bearer)
  - ImageGallery.vue : grille thumbnails + lightbox plein-écran avec
    nav clavier (← → Esc), bouton supprimer par image en mode admin
  - ImageUploader.vue : drag-drop + file picker, multi-fichiers en série,
    validation taille côté client, légende commune optionnelle
  - ProjectDetailPage : nouvel onglet Images entre Roadmap et Discussions

==== Dashboard /admin ====

5 nouvelles pages sous /admin :
  - /admin                           : KPI + répartition catégorie + flux activité
  - /admin/projects                  : table CRUD (recherche + filtres status/categorie),
                                       actions Éditer / Archiver / Supprimer par ligne
  - /admin/projects/:slug/edit       : form complet d'édition (envoie un diff au PATCH)
  - /admin/members                   : annuaire avec recherche + Supprimer
  - /admin/categories                : table avec compte de projets ;
                                       Supprimer désactivé si la catégorie est utilisée
  - AdminSubNav.vue                  : 4 onglets en haut de chaque page admin

Garde d'auth :
  - beforeEnter sur les routes /admin/* (sauf /admin/login)
  - Redirige vers /admin/login?next=<url> si pas de token
  - Après login, retour automatique vers next= (sinon /admin)

Sidebar :
  - Nouvelle section "Administration" visible UNIQUEMENT quand le token
    est en localStorage, avec un lien vers le dashboard

Vérifié localement
  - Build vue-tsc : 91 modules, OK
  - Upload disque : fichier PNG stocké dans public/uploads/, accessible via /uploads/
  - GET /api/projects/nos-ecoles/images : 2 entrées renvoyées (1 URL + 1 disque)
  - Toutes les routes /admin/* répondent 200 via le proxy Vite

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:07:59 +02:00

46 lines
1.8 KiB
Plaintext

# infolab.ledonut-marseille.org — Le Donut Infolab (API JSON + SPA Vue)
#
# - /api/* -> reverse_proxy vers le conteneur donut-infolab (PHP/Apache, 127.0.0.1:8003)
# - tout le reste -> SPA Vue (build statique dans /srv/donut/web/infolab-front/dist)
# avec fallback /index.html pour le router HTML5 (Vue Router en mode history).
infolab.ledonut-marseille.org {
encode gzip zstd
# Headers de sécurité de base
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
# API JSON et images uploadées -> conteneur PHP (sur donut-net, exposé 127.0.0.1:8003).
# /uploads/* contient les images de la galerie projet stockées sur disque par
# l'admin via POST /api/projects/:slug/images (multipart). Apache du conteneur
# les sert en static — pas besoin de PHP juste pour servir un fichier.
@apiOrUploads path /api/* /uploads/*
handle @apiOrUploads {
reverse_proxy 127.0.0.1:8003 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# SPA Vue : sert dist/ avec fallback index.html pour le routing client-side.
handle {
root * /srv/donut/web/infolab-front/dist
try_files {path} /index.html
file_server {
precompressed gzip
}
# Cache long pour les assets fingerprintés Vite, court pour index.html.
@assets path /assets/*
header @assets Cache-Control "public, max-age=31536000, immutable"
@html path /index.html
header @html Cache-Control "no-cache, no-store, must-revalidate"
}
}