From 55a30b8db8a514df3f67e790a2ee317393cc414e Mon Sep 17 00:00:00 2001 From: Leon Morival Date: Fri, 10 Jul 2026 15:28:23 +0200 Subject: [PATCH] feat: ci/cd --- .dockerignore | 6 + .gitea/workflows/cd.yml | 112 +++++------------ .gitea/workflows/ci.yml | 142 +++++++++++++-------- .gitignore | 4 + Dockerfile | 56 +++++++++ compose.prod.yaml | 114 +++++------------ composer.json | 1 + composer.lock | 266 +++++++++++++++++++++++++++++++++++++++- config/octane.php | 224 +++++++++++++++++++++++++++++++++ docker/Dockerfile.prod | 84 ------------- docker/nginx.prod.conf | 48 -------- docker/php.prod.ini | 27 ---- 12 files changed, 707 insertions(+), 377 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 config/octane.php delete mode 100644 docker/Dockerfile.prod delete mode 100644 docker/nginx.prod.conf delete mode 100644 docker/php.prod.ini diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3ea1888 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.env +node_modules +vendor +storage/logs +docker-compose*.yml diff --git a/.gitea/workflows/cd.yml b/.gitea/workflows/cd.yml index 1ad6414..4943733 100644 --- a/.gitea/workflows/cd.yml +++ b/.gitea/workflows/cd.yml @@ -1,98 +1,50 @@ -# ============================================================ -# CD — Déploiement Continu -# Déclenche un déploiement uniquement sur push main. -# -# Le runner Gitea Actions tourne directement sur le serveur. -# Aucun SSH nécessaire — les commandes s'exécutent localement. -# ============================================================ - name: CD - on: - push: + workflow_run: + workflows: + - CI branches: - main - + types: + - completed jobs: deploy: name: 🚀 Déploiement production + if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest - steps: - - # ── 1. Mise à jour du code ─────────────────────────────── - - name: 🔄 Mise à jour du code + - name: 🔐 Configuration SSH run: | - set -e - cd /data/docker/landing-api + mkdir -p ~/.ssh + echo "${{ secrets.DEPLOY_SSH_KEY }}" \ + > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 - echo "🔄 Pull depuis Gitea..." - git fetch origin main - git reset --hard origin/main + ssh-keyscan \ + -H "${{ secrets.DEPLOY_HOST }}" \ + >> ~/.ssh/known_hosts - # ── 2. Build de l'image Docker ─────────────────────────── - - name: 🐳 Build image Docker + - name: 🚀 Deploy Docker run: | - set -e - cd /data/docker/landing-api + ssh \ + -i ~/.ssh/id_ed25519 \ + ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << 'EOF' + set -e + echo "📂 Projet" + cd /data/docker/landing-api + echo "⬇️ Pull nouvelle image" + docker compose pull + echo "🚀 Redémarrage" + docker compose up -d + echo "🗄️ Migration Laravel" - echo "🏗️ Build de l'image de production..." - docker compose -f compose.prod.yaml build --no-cache app + docker compose exec -T app \ + php artisan migrate --force - # ── 3. Démarrage / redémarrage des containers ──────────── - - name: 🚀 Déploiement des containers - run: | - set -e - cd /data/docker/landing-api + echo "🧹 Nettoyage" - echo "🚀 Redémarrage des services..." - docker compose -f compose.prod.yaml up -d --remove-orphans + docker image prune -f - # ── 4. Migrations base de données ──────────────────────── - - name: 🗄️ Migrations base de données - run: | - set -e - cd /data/docker/landing-api + echo "✅ Déploiement terminé" - echo "⏳ Attente que la BDD soit prête..." - sleep 5 - - echo "🗄️ Exécution des migrations..." - docker compose -f compose.prod.yaml exec -T app php artisan migrate --force - - # ── 5. Optimisations Laravel ───────────────────────────── - - name: ⚙️ Optimisations Laravel - run: | - set -e - cd /data/docker/landing-api - - echo "⚙️ Mise en cache des configs, routes, vues..." - docker compose -f compose.prod.yaml exec -T app php artisan config:cache - docker compose -f compose.prod.yaml exec -T app php artisan route:cache - docker compose -f compose.prod.yaml exec -T app php artisan view:cache - docker compose -f compose.prod.yaml exec -T app php artisan event:cache - - echo "🔄 Redémarrage des workers..." - docker compose -f compose.prod.yaml exec -T app php artisan queue:restart - - # ── 6. Nettoyage des anciennes images ──────────────────── - - name: 🧹 Nettoyage Docker - run: | - docker image prune -f - - # ── 7. Résultat ────────────────────────────────────────── - - name: 🎉 Déploiement réussi - if: success() - run: | - echo "✅ Application déployée avec succès !" - echo "🔗 Commit : ${{ github.sha }}" - echo "👤 Par : ${{ github.actor }}" - - - name: ❌ Déploiement échoué - if: failure() - run: | - echo "❌ Le déploiement a échoué — rollback recommandé" - echo "🔗 Commit : ${{ github.sha }}" - # Affiche les logs des containers en erreur pour debug - cd /data/docker/landing-api - docker compose -f compose.prod.yaml logs --tail=50 + EOF diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9bd7a11..80097bc 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,78 +1,116 @@ -# ============================================================ -# CI — Intégration Continue -# Se déclenche sur chaque push et chaque Pull Request. -# Lance les tests PHPUnit avec SQLite en mémoire (rapide). -# ============================================================ - name: CI on: push: - branches: - - '**' # tous les branches - pull_request: branches: - main - develop + pull_request: + branches: + - main jobs: test: - name: 🧪 Tests Laravel (PHP ${{ matrix.php }}) + name: 🧪 Tests Laravel runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - php: ['8.3'] # Ajoute d'autres versions si tu veux tester plusieurs PHP - steps: - # ── 1. Récupère le code ───────────────────────────────── - - name: 📥 Checkout du code + - name: 📥 Checkout uses: actions/checkout@v4 - - # ── 2. Configure PHP avec les extensions nécessaires ──── - - name: 🐘 Setup PHP ${{ matrix.php }} + - name: 🐘 Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php }} - extensions: mbstring, bcmath, sqlite3, pdo_sqlite, pgsql, pdo_pgsql, xml, curl, zip, gd, intl - coverage: none # met "xdebug" ici si tu veux du coverage + php-version: '8.3' + extensions: > + bcmath, + ctype, + curl, + dom, + fileinfo, + mbstring, + openssl, + pdo, + tokenizer, + xml, + zip + coverage: none + - # ── 3. Cache des dépendances Composer ─────────────────── - name: 📦 Cache Composer uses: actions/cache@v4 with: - path: vendor - key: composer-${{ matrix.php }}-${{ hashFiles('composer.lock') }} - restore-keys: composer-${{ matrix.php }}- + path: ~/.composer/cache + key: composer-${{ hashFiles('composer.lock') }} - # ── 4. Installe les dépendances PHP ───────────────────── - - name: 📦 Installation Composer - run: composer install --no-interaction --prefer-dist --optimize-autoloader --no-scripts - # ── 5. Prépare l'environnement .env de test ───────────── - - name: ⚙️ Configuration .env + - name: 📦 Install Composer dependencies run: | + composer install \ + --no-interaction \ + --prefer-dist \ + --no-progress + + + - name: ⚙️ Prepare Laravel + run: | + cp .env.example .env - # On écrase les variables critiques pour les tests - echo "APP_ENV=testing" >> .env - echo "APP_KEY=" >> .env - echo "DB_CONNECTION=sqlite" >> .env - echo "DB_DATABASE=:memory:" >> .env - echo "QUEUE_CONNECTION=sync" >> .env - echo "SESSION_DRIVER=array" >> .env - echo "CACHE_STORE=array" >> .env - echo "MAIL_MAILER=array" >> .env - echo "BROADCAST_CONNECTION=null" >> .env - # ── 6. Génère la clé d'application ────────────────────── - - name: 🔑 Génération de la clé APP_KEY - run: php artisan key:generate --ansi + php artisan key:generate - # ── 7. Exécute les migrations (SQLite :memory:) ───────── - - name: 🗄️ Migrations - run: php artisan migrate --force --ansi + touch database/database.sqlite - # ── 8. Lance les tests ─────────────────────────────────── - - name: ✅ Tests PHPUnit - run: php artisan test --ansi + php artisan migrate --force + + + - name: 🧪 Run PHPUnit + run: | + php artisan test + + + + docker: + + + name: 🐳 Build & Push Image + + needs: test + + if: github.ref == 'refs/heads/main' + + + runs-on: ubuntu-latest + + + steps: + + + - name: 📥 Checkout + uses: actions/checkout@v4 + + + + - name: 🔐 Login Registry Gitea + run: | + + docker login git.leonmorival.xyz \ + -u "${{ secrets.REGISTRY_USER }}" \ + -p "${{ secrets.REGISTRY_PASSWORD }}" + + + + - name: 🏗️ Build image + run: | + + docker build \ + -t git.leonmorival.xyz/leonm/landing-api:latest \ + -t git.leonmorival.xyz/leonm/landing-api:${{ github.sha }} \ + . + + + + - name: 📤 Push image + run: | + + docker push git.leonmorival.xyz/leonm/landing-api:latest + + docker push git.leonmorival.xyz/leonm/landing-api:${{ github.sha }} diff --git a/.gitignore b/.gitignore index 7be55e2..0e7fda1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ _ide_helper.php Homestead.json Homestead.yaml Thumbs.db + +**/caddy +frankenphp +frankenphp-worker.php diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fe03efd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,56 @@ +FROM dunglas/frankenphp:php8.3-alpine + + +WORKDIR /app + + +RUN install-php-extensions \ + pdo_pgsql \ + opcache \ + pcntl \ + zip \ + intl + + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + + +COPY composer.json composer.lock ./ + + +RUN composer install \ + --no-dev \ + --no-interaction \ + --prefer-dist \ + --optimize-autoloader + + +COPY . . + + +RUN npm install +RUN npm run build + + +RUN php artisan octane:install --server=frankenphp || true + + +RUN php artisan config:cache +RUN php artisan route:cache +RUN php artisan view:cache + + +RUN chown -R www-data:www-data storage bootstrap/cache + + +EXPOSE 8000 + + +CMD [ + "php", + "artisan", + "octane:start", + "--server=frankenphp", + "--host=0.0.0.0", + "--port=8000" +] diff --git a/compose.prod.yaml b/compose.prod.yaml index dfec64e..c9d7c09 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -1,102 +1,46 @@ -# ============================================================ -# Docker Compose — Production -# -# Architecture : -# [Nginx Proxy Manager] ──► [FrankenPHP app:80] -# │ -# [PostgreSQL] -# [Queue Worker] -# -# Le réseau "proxy" est externe (géré par Nginx Proxy Manager). -# Dans NPM, configure un proxy host vers : http://landing-api-app:80 -# -# Usage : -# docker compose -f compose.prod.yaml up -d -# ============================================================ - services: - # ── Application FrankenPHP (web + PHP tout-en-un) ─────── app: - build: - context: . - dockerfile: docker/Dockerfile.prod - image: landing-api:latest - container_name: landing-api-app + image: git.leonmorival.xyz/leon/landing-api:latest + container_name: landing-api restart: unless-stopped + env_file: - .env - environment: - APP_ENV: production - APP_DEBUG: "false" - SERVER_NAME: ":80" # FrankenPHP écoute sur le port 80 - volumes: - - app_storage:/app/storage/app - - app_logs:/app/storage/logs - networks: - - proxy # réseau NPM pour être accessible depuis l'extérieur - - internal # réseau interne pour parler à PostgreSQL - depends_on: - pgsql: - condition: service_healthy - # ── Base de données PostgreSQL ────────────────────────── - pgsql: - image: postgres:17-alpine - container_name: landing-api-pgsql - restart: unless-stopped - environment: - POSTGRES_DB: "${DB_DATABASE}" - POSTGRES_USER: "${DB_USERNAME}" - POSTGRES_PASSWORD: "${DB_PASSWORD}" - PGDATA: /var/lib/postgresql/data/pgdata - volumes: - - pgsql_data:/var/lib/postgresql/data networks: - - internal # PostgreSQL n'est PAS exposé sur le réseau proxy - healthcheck: - test: ["CMD-SHELL", "pg_isready -q -d ${DB_DATABASE} -U ${DB_USERNAME}"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 30s + - proxy + - database - # ── Worker Laravel Queue ──────────────────────────────── - queue: - build: - context: . - dockerfile: docker/Dockerfile.prod - image: landing-api:latest - container_name: landing-api-queue - restart: unless-stopped - command: ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"] - env_file: - - .env - environment: - APP_ENV: production - APP_DEBUG: "false" volumes: - - app_storage:/app/storage/app - - app_logs:/app/storage/logs + - storage:/var/www/html/storage + + + postgres: + image: postgres:16 + container_name: landing-api-postgres + restart: unless-stopped + + environment: + POSTGRES_DB: ${DB_DATABASE} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_PASSWORD: ${DB_PASSWORD} + + volumes: + - postgres-data:/var/lib/postgresql/data + networks: - - internal - depends_on: - pgsql: - condition: service_healthy + - database + + +volumes: + storage: + postgres-data: + networks: - # Réseau externe partagé avec Nginx Proxy Manager proxy: external: true - # Réseau interne isolé (app ↔ pgsql ↔ queue) - internal: + database: driver: bridge - -volumes: - pgsql_data: - driver: local - app_storage: - driver: local - app_logs: - driver: local diff --git a/composer.json b/composer.json index c6ea95c..4125a12 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "php": "^8.3", "filament/filament": "^5.0", "laravel/framework": "^13.8", + "laravel/octane": "^2.17", "laravel/sanctum": "^4.0", "laravel/tinker": "^3.0" }, diff --git a/composer.lock b/composer.lock index d1d00c4..04c328e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "10b22fffd5baa69a8732cb3894b68663", + "content-hash": "5bbf65aa881d14aff56a15114641ac54", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -2089,6 +2089,94 @@ }, "time": "2026-05-28T20:35:55+00:00" }, + { + "name": "laminas/laminas-diactoros", + "version": "3.8.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "60c182916b2749480895601649563970f3f12ec4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/60c182916b2749480895601649563970f3f12ec4", + "reference": "60c182916b2749480895601649563970f3f12ec4", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "conflict": { + "amphp/amp": "<2.6.4" + }, + "provide": { + "psr/http-factory-implementation": "^1.0", + "psr/http-message-implementation": "^1.1 || ^2.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "ext-gd": "*", + "ext-libxml": "*", + "http-interop/http-factory-tests": "^2.2.0", + "laminas/laminas-coding-standard": "~3.1.0", + "php-http/psr7-integration-tests": "^1.4.0", + "phpunit/phpunit": "^10.5.36", + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13" + }, + "type": "library", + "extra": { + "laminas": { + "module": "Laminas\\Diactoros", + "config-provider": "Laminas\\Diactoros\\ConfigProvider" + } + }, + "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], + "psr-4": { + "Laminas\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-17", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-diactoros/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-diactoros/issues", + "rss": "https://github.com/laminas/laminas-diactoros/releases.atom", + "source": "https://github.com/laminas/laminas-diactoros" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-10-12T15:31:36+00:00" + }, { "name": "laravel/framework", "version": "v13.19.0", @@ -2313,6 +2401,95 @@ }, "time": "2026-07-07T14:13:33+00:00" }, + { + "name": "laravel/octane", + "version": "v2.17.5", + "source": { + "type": "git", + "url": "https://github.com/laravel/octane.git", + "reference": "058ae4d7109eed40836dc42960f9388b9bf71f73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/octane/zipball/058ae4d7109eed40836dc42960f9388b9bf71f73", + "reference": "058ae4d7109eed40836dc42960f9388b9bf71f73", + "shasum": "" + }, + "require": { + "laminas/laminas-diactoros": "^3.0", + "laravel/framework": "^10.10.1|^11.0|^12.0|^13.0", + "laravel/prompts": "^0.1.24|^0.2.0|^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "nesbot/carbon": "^2.66.0|^3.0", + "php": "^8.1.0", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/psr-http-message-bridge": "^2.2.0|^6.4|^7.0|^8.0" + }, + "conflict": { + "spiral/roadrunner": "<2023.1.0", + "spiral/roadrunner-cli": "<2.6.0", + "spiral/roadrunner-http": "<3.3.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.6.1", + "inertiajs/inertia-laravel": "^1.3.2|^2.0", + "laravel/scout": "^10.2.1", + "laravel/socialite": "^5.6.1", + "livewire/livewire": "^2.12.3|^3.0", + "nunomaduro/collision": "^6.4.0|^7.5.2|^8.0", + "orchestra/testbench": "^8.21|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.1.7", + "phpunit/phpunit": "^10.4|^11.5|^12.0|^13.0", + "spiral/roadrunner-cli": "^2.6.0", + "spiral/roadrunner-http": "^3.3.0" + }, + "bin": [ + "bin/roadrunner-worker", + "bin/swoole-server" + ], + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Octane": "Laravel\\Octane\\Facades\\Octane" + }, + "providers": [ + "Laravel\\Octane\\OctaneServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Octane\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Supercharge your Laravel application's performance.", + "keywords": [ + "frankenphp", + "laravel", + "octane", + "roadrunner", + "swoole" + ], + "support": { + "issues": "https://github.com/laravel/octane/issues", + "source": "https://github.com/laravel/octane" + }, + "time": "2026-06-04T09:05:08+00:00" + }, { "name": "laravel/prompts", "version": "v0.3.21", @@ -7300,6 +7477,93 @@ ], "time": "2026-05-29T05:06:50+00:00" }, + { + "name": "symfony/psr-http-message-bridge", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/psr-http-message-bridge.git", + "reference": "67fd34de15ded1763aa1e330fe345f080a94022c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/67fd34de15ded1763aa1e330fe345f080a94022c", + "reference": "67fd34de15ded1763aa1e330fe345f080a94022c", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "psr/http-message": "^1.0|^2.0", + "symfony/http-foundation": "^7.4|^8.0" + }, + "conflict": { + "php-http/discovery": "<1.15" + }, + "require-dev": { + "nyholm/psr7": "^1.1", + "php-http/discovery": "^1.15", + "psr/log": "^1.1.4|^2|^3", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\PsrHttpMessage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "PSR HTTP message bridge", + "homepage": "https://symfony.com", + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], + "support": { + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v8.1.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-29T05:06:50+00:00" + }, { "name": "symfony/routing", "version": "v8.1.0", diff --git a/config/octane.php b/config/octane.php new file mode 100644 index 0000000..8cfba01 --- /dev/null +++ b/config/octane.php @@ -0,0 +1,224 @@ + env('OCTANE_SERVER', 'roadrunner'), + + /* + |-------------------------------------------------------------------------- + | Force HTTPS + |-------------------------------------------------------------------------- + | + | When this configuration value is set to "true", Octane will inform the + | framework that all absolute links must be generated using the HTTPS + | protocol. Otherwise your links may be generated using plain HTTP. + | + */ + + 'https' => env('OCTANE_HTTPS', false), + + /* + |-------------------------------------------------------------------------- + | Octane Listeners + |-------------------------------------------------------------------------- + | + | All of the event listeners for Octane's events are defined below. These + | listeners are responsible for resetting your application's state for + | the next request. You may even add your own listeners to the list. + | + */ + + 'listeners' => [ + WorkerStarting::class => [ + EnsureUploadedFilesAreValid::class, + EnsureUploadedFilesCanBeMoved::class, + ], + + RequestReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + ...Octane::prepareApplicationForNextRequest(), + // + ], + + RequestHandled::class => [ + // + ], + + RequestTerminated::class => [ + // FlushUploadedFiles::class, + ], + + TaskReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + // + ], + + TaskTerminated::class => [ + // + ], + + TickReceived::class => [ + ...Octane::prepareApplicationForNextOperation(), + // + ], + + TickTerminated::class => [ + // + ], + + OperationTerminated::class => [ + FlushOnce::class, + FlushTemporaryContainerInstances::class, + // DisconnectFromDatabases::class, + // CollectGarbage::class, + ], + + WorkerErrorOccurred::class => [ + ReportException::class, + StopWorkerIfNecessary::class, + ], + + WorkerStopping::class => [ + CloseMonologHandlers::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Warm / Flush Bindings + |-------------------------------------------------------------------------- + | + | The bindings listed below will either be pre-warmed when a worker boots + | or they will be flushed before every new request. Flushing a binding + | will force the container to resolve that binding again when asked. + | + */ + + 'warm' => [ + ...Octane::defaultServicesToWarm(), + ], + + 'flush' => [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Octane Swoole Tables + |-------------------------------------------------------------------------- + | + | While using Swoole, you may define additional tables as required by the + | application. These tables can be used to store data that needs to be + | quickly accessed by other workers on the particular Swoole server. + | + */ + + 'tables' => [ + 'example:1000' => [ + 'name' => 'string:1000', + 'votes' => 'int', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Octane Swoole Cache Table + |-------------------------------------------------------------------------- + | + | While using Swoole, you may leverage the Octane cache, which is powered + | by a Swoole table. You may set the maximum number of rows as well as + | the number of bytes per row using the configuration options below. + | + */ + + 'cache' => [ + 'rows' => 1000, + 'bytes' => 10000, + ], + + /* + |-------------------------------------------------------------------------- + | File Watching + |-------------------------------------------------------------------------- + | + | The following list of files and directories will be watched when using + | the --watch option offered by Octane. If any of the directories and + | files are changed, Octane will automatically reload your workers. + | + */ + + 'watch' => [ + 'app', + 'bootstrap', + 'config/**/*.php', + 'database/**/*.php', + 'public/**/*.php', + 'resources/**/*.php', + 'routes', + 'composer.lock', + '.env', + ], + + /* + |-------------------------------------------------------------------------- + | Garbage Collection Threshold + |-------------------------------------------------------------------------- + | + | When executing long-lived PHP scripts such as Octane, memory can build + | up before being cleared by PHP. You can force Octane to run garbage + | collection if your application consumes this amount of megabytes. + | + */ + + 'garbage' => 50, + + /* + |-------------------------------------------------------------------------- + | Maximum Execution Time + |-------------------------------------------------------------------------- + | + | The following setting configures the maximum execution time for requests + | being handled by Octane. You may set this value to 0 to indicate that + | there isn't a specific time limit on Octane request execution time. + | + */ + + 'max_execution_time' => 30, + +]; diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod deleted file mode 100644 index 407044c..0000000 --- a/docker/Dockerfile.prod +++ /dev/null @@ -1,84 +0,0 @@ -# ============================================================ -# Dockerfile — Production (FrankenPHP) -# FrankenPHP = serveur web + PHP runtime tout-en-un -# Remplace PHP-FPM + Nginx en un seul container -# ============================================================ - -FROM dunglas/frankenphp:php8.3-alpine AS base - -# Extensions PHP nécessaires pour Laravel -RUN apk add --no-cache \ - postgresql-dev \ - libpng-dev \ - libjpeg-turbo-dev \ - freetype-dev \ - libzip-dev \ - oniguruma-dev \ - icu-dev \ - curl \ - unzip \ - git \ - && docker-php-ext-configure gd --with-freetype --with-jpeg \ - && docker-php-ext-install \ - pdo \ - pdo_pgsql \ - pgsql \ - mbstring \ - zip \ - gd \ - bcmath \ - intl \ - opcache \ - pcntl - -# Composer -COPY --from=composer:2 /usr/bin/composer /usr/bin/composer - -# ── Stage Build (Node pour compiler les assets Vite) ──────── -FROM base AS builder - -WORKDIR /app - -# Dépendances PHP (sans dev) -COPY composer.json composer.lock ./ -RUN composer install \ - --no-interaction \ - --no-dev \ - --prefer-dist \ - --optimize-autoloader \ - --no-scripts - -# Dépendances Node + build assets -COPY package.json package-lock.json* ./ -RUN apk add --no-cache nodejs npm \ - && npm ci --ignore-scripts - -COPY . . - -# Génère les assets Vite -RUN npm run build - -# Post-scripts Composer -RUN composer run-script post-autoload-dump || true - -# ── Stage Final ────────────────────────────────────────────── -FROM base AS production - -WORKDIR /app - -# Copie tout depuis le builder -COPY --from=builder /app /app - -# Config PHP optimisée prod -COPY docker/php.prod.ini /usr/local/etc/php/conf.d/99-prod.ini - -# Permissions sur storage et bootstrap/cache -RUN mkdir -p storage/logs storage/framework/cache storage/framework/sessions storage/framework/views bootstrap/cache \ - && chown -R www-data:www-data storage bootstrap/cache \ - && chmod -R 775 storage bootstrap/cache - -# FrankenPHP écoute sur le port 80 -EXPOSE 80 - -# FrankenPHP sert le dossier public/ de Laravel -CMD ["frankenphp", "php-server", "--root", "/app/public"] diff --git a/docker/nginx.prod.conf b/docker/nginx.prod.conf deleted file mode 100644 index 41a6f3b..0000000 --- a/docker/nginx.prod.conf +++ /dev/null @@ -1,48 +0,0 @@ -server { - listen 80; - server_name _; - root /var/www/html/public; - index index.php; - - # Sécurité — masque la version Nginx - server_tokens off; - - # Taille max des uploads - client_max_body_size 50M; - - # Logs - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log warn; - - # Gzip - gzip on; - gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; - gzip_min_length 256; - - # Assets statiques — cache long terme - location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|webp)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } - - # Route principale Laravel - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - # PHP-FPM - location ~ \.php$ { - fastcgi_pass app:9000; - fastcgi_index index.php; - fastcgi_buffers 16 16k; - fastcgi_buffer_size 32k; - fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; - include fastcgi_params; - } - - # Bloque l'accès aux fichiers cachés - location ~ /\. { - deny all; - } -} diff --git a/docker/php.prod.ini b/docker/php.prod.ini deleted file mode 100644 index 0df5537..0000000 --- a/docker/php.prod.ini +++ /dev/null @@ -1,27 +0,0 @@ -; Configuration PHP optimisée pour la production - -[PHP] -; Sécurité -expose_php = Off -display_errors = Off -display_startup_errors = Off -log_errors = On -error_log = /var/www/html/storage/logs/php-errors.log - -; Performance -memory_limit = 256M -max_execution_time = 60 -max_input_time = 60 -post_max_size = 50M -upload_max_filesize = 50M - -; OPcache -opcache.enable = 1 -opcache.enable_cli = 0 -opcache.memory_consumption = 128 -opcache.interned_strings_buffer = 16 -opcache.max_accelerated_files = 10000 -opcache.revalidate_freq = 0 -opcache.validate_timestamps = 0 -opcache.save_comments = 1 -opcache.fast_shutdown = 1