diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8fed889 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,77 @@ +# +# Fichiers exclus du contexte de build Docker. +# Reduit le temps de build et evite de faire entrer des fichiers sensibles +# ou non pertinents dans les couches d'image. +# + +# === Git === +.git +.gitignore +.gitattributes +.githooks + +# === CI/CD === +.github + +# === Methodologie / outils dev === +.claude +_byan +_byan-output +.mcp.json + +# === Documentation et notes === +docs +README.md +*.md + +# === Secrets locaux === +.env +.env.local +.env.*.local + +# === Tests (pas dans image de prod) === +tests +phpunit.xml +phpunit.phar +.phpunit.result.cache + +# === Scripts locaux === +scripts +Makefile + +# === Composer (non utilise mais safety) === +vendor +composer.lock +composer.phar +composer.json + +# === Node (au cas ou) === +node_modules +npm-debug.log +yarn-error.log + +# === IDE === +.idea +.vscode +*.swp +*.swo +*~ + +# === OS === +.DS_Store +Thumbs.db + +# === Logs / backups / volumes locaux === +*.log +logs +backups +docker-data +data + +# === Build artifacts === +dist +build + +# === Docker lui-meme === +docker-compose.yml +docker-compose.*.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0e2cb55 --- /dev/null +++ b/.env.example @@ -0,0 +1,110 @@ +# +# Wakdo - template de configuration +# +# Usage : +# cp .env.example .env +# Editer .env (gitignore) avec les valeurs reelles. +# +# Audience : +# Destine a l'auteur, au jury et aux contributeurs futurs. +# +# Modele de deploiement : +# Ce projet tourne sur serveur derriere un reverse proxy Traefik. Il n'y a +# pas de binding de ports hote : l'acces se fait uniquement via les FQDN +# configures ci-dessous et routes par Traefik (reseau admin_proxy). +# Les distinctions dev / staging / prod se font par FQDN distincts +# (ex : .dev.acadenice.fr vs .acadenice.fr) et par .env dedie. +# + +# =================================================================== +# Environnement applicatif +# =================================================================== + +APP_ENV=dev # dev | staging | prod +APP_DEBUG=true # true en dev, false en prod +APP_TIMEZONE=Europe/Paris + +# URL publique de la borne (Bloc 1), doit pointer vers le FQDN Traefik. +# Placeholder example.com (RFC 2606) - a remplacer par le FQDN reel. +APP_URL_KIOSK=https://kiosk.example.com + +# URL publique du back-office + API (Bloc 2). +# Placeholder example.com (RFC 2606) - a remplacer par le FQDN reel. +APP_URL_ADMIN=https://admin.example.com + +# =================================================================== +# Base de donnees (MariaDB) +# =================================================================== +# Valeurs ci-dessous = PLACEHOLDERS. Remplacer par des mots de passe forts. +# Pas accessible depuis l'exterieur : le service wakdo-db est sur le reseau +# interne uniquement, aucun port exposé a l'hote. + +DB_HOST=wakdo-db # nom du service docker-compose +DB_PORT=3306 +DB_NAME=wakdo +DB_USER=wakdo +DB_PASSWORD=change_me_strong_password +DB_ROOT_PASSWORD=change_me_root_password + +# =================================================================== +# Sessions +# =================================================================== + +SESSION_LIFETIME_IDLE=14400 # 4h en secondes - idle timeout +SESSION_LIFETIME_ABSOLUTE=36000 # 10h en secondes - absolute timeout +SESSION_NAME=WAKDO_SID # nom du cookie (evite PHPSESSID) + +# =================================================================== +# Securite +# =================================================================== + +# Origine autorisee pour les requetes CORS de l'API. +# Doit correspondre exactement a APP_URL_KIOSK (pas de wildcard). +CORS_ALLOWED_ORIGIN=https://kiosk.example.com + +# Algorithme de hashage mot de passe (password_hash PHP). +# argon2id recommande depuis PHP 7.3 pour les nouveaux projets. +PASSWORD_ALGO=argon2id + +# =================================================================== +# Upload images produits +# =================================================================== + +UPLOAD_MAX_SIZE_MB=5 +UPLOAD_ALLOWED_MIME=image/jpeg,image/png,image/webp + +# =================================================================== +# Cron (fenetre de maintenance 01h30 - 09h30) +# =================================================================== +# Les jobs sont definis dans docker/cron/crontab. Ici uniquement le TZ. + +CRON_TIMEZONE=Europe/Paris + +# =================================================================== +# Exposition via Traefik +# =================================================================== +# FQDN consommes par les labels docker-compose.yml pour generer les routes +# Traefik et les certificats TLS (Traefik gere le resolver par defaut). +# Le Traefik de l'hote prend en charge Let's Encrypt automatiquement. + +TRAEFIK_DOMAIN_KIOSK=kiosk.example.com +TRAEFIK_DOMAIN_ADMIN=admin.example.com + +# =================================================================== +# Reseau Docker externe du reverse proxy +# =================================================================== +# Nom du reseau Docker externe auquel le service web doit se connecter +# pour etre expose par le reverse proxy de l'hote. +# +# Adapter selon votre infrastructure. Valeurs courantes : +# traefik_proxy - convention neutre (placeholder) +# traefik_public - convention doc Traefik +# traefik - setups simples +# proxy - autre convention frequente +# +# Le reseau doit exister AVANT 'make init' (cree par votre stack de +# reverse proxy, ou manuellement : docker network create ). +# La cible 'make init' echoue proprement avec un message d'aide si le +# reseau est introuvable. + +REVERSE_PROXY_NETWORK=traefik_proxy diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..783d50a --- /dev/null +++ b/Makefile @@ -0,0 +1,210 @@ +# +# Wakdo - Makefile d'orchestration locale +# +# Conventions : +# - Une cible = une action unitaire. Les cibles composites sont commentees. +# - Chaque cible est documentee par un `## description` pour auto-help. +# - Echec sur erreur (set -e implicite via bash recipes + pipefail). +# +# Documentation : +# make help +# + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -eu -o pipefail -c + +# === Configuration === + +# Chargement du .env s'il existe (variables Make + export pour docker compose) +ifneq (,$(wildcard .env)) +include .env +export +endif + +# Prefixe du projet compose (utilise pour nommer les containers) +PROJECT := wakdo + +# Nom du fichier compose (override possible : make up COMPOSE_FILE=docker-compose.prod.yml) +COMPOSE_FILE := docker-compose.yml +COMPOSE := docker compose -f $(COMPOSE_FILE) -p $(PROJECT) + +# Services docker-compose +SERVICE_WEB := wakdo-web +SERVICE_APP := wakdo-app +SERVICE_DB := wakdo-db +SERVICE_CRON := wakdo-cron + +# === Meta === + +.DEFAULT_GOAL := help +.PHONY: help +help: ## Liste toutes les cibles disponibles avec leur description + @echo "Wakdo - cibles Make disponibles :" + @echo "" + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[1m%-22s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort + @echo "" + +# === Orchestration principale === + +.PHONY: init +init: ## Build et demarre toute la stack en une commande (Cr RNCP 7.c.4) + @test -f .env || { echo "ERREUR: .env manquant. Executer : cp .env.example .env"; exit 1; } + @$(MAKE) --no-print-directory check-env + @echo "[init] Verification du reseau docker '$(REVERSE_PROXY_NETWORK)'..." + @docker network inspect $(REVERSE_PROXY_NETWORK) >/dev/null 2>&1 || { \ + echo "ERREUR: reseau docker '$(REVERSE_PROXY_NETWORK)' introuvable."; \ + echo " - Si un Traefik est installe sur l'hote, verifier le nom de son reseau ;"; \ + echo " - Adapter REVERSE_PROXY_NETWORK dans .env en consequence ;"; \ + echo " - Sinon creer le reseau manuellement :"; \ + echo " docker network create $(REVERSE_PROXY_NETWORK)"; \ + exit 1; } + @echo "[init] Build des images..." + @$(COMPOSE) build + @echo "[init] Demarrage des services..." + @$(COMPOSE) up -d + @echo "[init] Attente de la base de donnees..." + @$(MAKE) --no-print-directory wait-db + @echo "[init] Execution des migrations..." + @$(MAKE) --no-print-directory migrate + @echo "[init] Stack operationnelle." + @$(COMPOSE) ps + +.PHONY: up +up: ## Demarre les services sans rebuild + @$(COMPOSE) up -d + +.PHONY: down +down: ## Arrete et supprime les containers (volumes preserves) + @$(COMPOSE) down + +.PHONY: stop +stop: ## Arrete les services sans les supprimer + @$(COMPOSE) stop + +.PHONY: restart +restart: ## Redemarre tous les services + @$(COMPOSE) restart + +.PHONY: build +build: ## Build les images (utilise le cache) + @$(COMPOSE) build + +.PHONY: rebuild +rebuild: ## Rebuild complet sans cache puis restart + @$(COMPOSE) build --no-cache + @$(COMPOSE) up -d + +# === Observabilite === + +.PHONY: ps +ps: ## Affiche le statut des services + @$(COMPOSE) ps + +.PHONY: logs +logs: ## Suit les logs de tous les services (Ctrl+C pour sortir) + @$(COMPOSE) logs -f --tail=100 + +.PHONY: logs-app +logs-app: ## Suit les logs du service applicatif PHP-FPM + @$(COMPOSE) logs -f --tail=100 $(SERVICE_APP) + +.PHONY: logs-web +logs-web: ## Suit les logs du service web Apache + @$(COMPOSE) logs -f --tail=100 $(SERVICE_WEB) + +.PHONY: logs-db +logs-db: ## Suit les logs de la base de donnees + @$(COMPOSE) logs -f --tail=100 $(SERVICE_DB) + +# === Acces shell === + +.PHONY: shell-app +shell-app: ## Ouvre un shell dans le container applicatif + @$(COMPOSE) exec $(SERVICE_APP) sh + +.PHONY: shell-db +shell-db: ## Ouvre le client mariadb dans le container de base de donnees + @$(COMPOSE) exec $(SERVICE_DB) mariadb -u root -p"$${DB_ROOT_PASSWORD}" + +.PHONY: shell-cron +shell-cron: ## Ouvre un shell dans le container cron + @$(COMPOSE) exec $(SERVICE_CRON) sh + +# === Verification env === + +.PHONY: check-env +check-env: ## Verifie que les variables critiques Wakdo sont definies dans .env + @missing=""; \ + for var in DB_PASSWORD DB_ROOT_PASSWORD REVERSE_PROXY_NETWORK TRAEFIK_DOMAIN_KIOSK TRAEFIK_DOMAIN_ADMIN APP_URL_KIOSK APP_URL_ADMIN CORS_ALLOWED_ORIGIN; do \ + if [ -z "$${!var:-}" ]; then missing="$$missing $$var"; fi; \ + done; \ + if [ -n "$$missing" ]; then \ + echo "ERREUR: variables manquantes dans .env :$$missing"; \ + echo "Conseil : si vous aviez un .env pre-existant (tooling externe),"; \ + echo " merger les variables manquantes depuis .env.example au lieu"; \ + echo " d'ecraser le fichier."; \ + exit 1; \ + fi + +# === Base de donnees === + +.PHONY: wait-db +wait-db: ## Attend que la base de donnees accepte les connexions (timeout 60s) + @echo "[wait-db] En attente de MariaDB..." + @timeout 60 bash -c 'until $(COMPOSE) exec -T $(SERVICE_DB) healthcheck.sh --connect --innodb_initialized >/dev/null 2>&1; do sleep 2; done' \ + || { echo "ERREUR: MariaDB ne repond pas apres 60s"; $(COMPOSE) logs --tail=50 $(SERVICE_DB); exit 1; } + @echo "[wait-db] OK" + +.PHONY: migrate +migrate: ## Applique les migrations SQL en attente [a venir] + @echo "[migrate] Pas encore implemente. Les migrations seront dans db/migrations/." + +.PHONY: seed +seed: ## Charge les donnees de demo [a venir] + @echo "[seed] Pas encore implemente. Les seeds seront dans db/seeds/." + +.PHONY: backup +backup: ## Genere un dump SQL horodate dans ./backups/ [a venir] + @echo "[backup] Pas encore implemente. Voir scripts/backup-db.sh a venir." + +# === Tests === + +.PHONY: test +test: ## Lance la suite complete de tests PHPUnit [a venir] + @echo "[test] Pas encore implemente. PHPUnit via .phar sera configure en P2." + +.PHONY: test-unit +test-unit: ## Lance uniquement les tests unitaires [a venir] + @echo "[test-unit] Pas encore implemente." + +.PHONY: test-integration +test-integration: ## Lance uniquement les tests d'integration [a venir] + @echo "[test-integration] Pas encore implemente." + +# === Qualite code === + +.PHONY: lint +lint: ## Lance php -l sur tous les fichiers src/ [a venir] + @echo "[lint] Pas encore implemente. PHP syntax check via php -l + outil de style en P2." + +# === Nettoyage === + +.PHONY: clean +clean: ## Stop + suppression containers + volumes (DESTRUCTIF, demande confirmation) + @read -p "Supprimer containers ET volumes (les donnees seront perdues) ? [y/N] " ans; \ + if [ "$$ans" = "y" ] || [ "$$ans" = "Y" ]; then \ + $(COMPOSE) down -v; \ + echo "[clean] Stack et volumes supprimes."; \ + else \ + echo "[clean] Annule."; \ + fi + +.PHONY: clean-force +clean-force: ## Version non interactive de clean (pour CI uniquement) + @$(COMPOSE) down -v + +# === Hooks Git === + +.PHONY: install-hooks +install-hooks: ## Installe les hooks git depuis .githooks/ [a venir] + @echo "[install-hooks] Pas encore implemente. Voir scripts/install-hooks.sh a venir."