adb2bd3fc1
==== 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>
46 lines
1.8 KiB
Plaintext
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"
|
|
}
|
|
}
|