diff --git a/.env.example b/.env.example index f70638ae..ec236d59 100644 --- a/.env.example +++ b/.env.example @@ -12,12 +12,12 @@ LOG_CHANNEL=stack LOG_STACK=single,nightwatch LOG_LEVEL=debug -DB_CONNECTION=mysql -DB_HOST=127.0.0.1 -DB_PORT=3306 +DB_CONNECTION=pgsql +DB_HOST=pgsql +DB_PORT=5432 DB_DATABASE=laravelcm -DB_USERNAME=root -DB_PASSWORD= +DB_USERNAME=sail +DB_PASSWORD=password BROADCAST_DRIVER=log CACHE_DRIVER=file diff --git a/app/Models/Plan.php b/app/Models/Plan.php index a2b8040a..5e72915c 100644 --- a/app/Models/Plan.php +++ b/app/Models/Plan.php @@ -9,8 +9,22 @@ use Illuminate\Database\Eloquent\Builder; use Laravelcm\Subscriptions\Models\Plan as Model; +/** + * @mixin Model + * + * @property-read PlanType $type + */ final class Plan extends Model { + protected $guarded = []; + + protected function casts(): array + { + return [ + 'type' => PlanType::class, + ]; + } + /** * @param Builder $query */ diff --git a/database/seeders/ChannelSeeder.php b/database/seeders/ChannelSeeder.php index b22fcb93..49092788 100644 --- a/database/seeders/ChannelSeeder.php +++ b/database/seeders/ChannelSeeder.php @@ -11,7 +11,11 @@ final class ChannelSeeder extends Seeder { public function run(): void { - $authentification = Channel::create(['name' => 'Authentification', 'slug' => 'authentification', 'color' => '#31c48d']); + $authentification = Channel::query()->create([ + 'name' => 'Authentification', + 'slug' => 'authentification', + 'color' => '#31c48d', + ]); $authentification->items()->createMany([ ['name' => 'Breeze', 'slug' => 'breeze'], ['name' => 'Fortify', 'slug' => 'fortify'], @@ -21,14 +25,22 @@ public function run(): void ['name' => 'UI', 'slug' => 'ui'], ]); - $javascript = Channel::create(['name' => 'JavaScript', 'slug' => 'javascript', 'color' => '#fdae3f']); + $javascript = Channel::query()->create([ + 'name' => 'JavaScript', + 'slug' => 'javascript', + 'color' => '#fdae3f', + ]); $javascript->items()->createMany([ ['name' => 'React', 'slug' => 'react'], ['name' => 'Vue.js', 'slug' => 'vue-js'], ['name' => 'Alpine.js', 'slug' => 'alpine-js'], ]); - $laravel = Channel::create(['name' => 'Laravel', 'slug' => 'laravel', 'color' => '#ff2d20']); + $laravel = Channel::query()->create([ + 'name' => 'Laravel', + 'slug' => 'laravel', + 'color' => '#ff2d20', + ]); $laravel->items()->createMany([ ['name' => 'Blade', 'slug' => 'blade'], ['name' => 'Eloquent', 'slug' => 'eloquent'], @@ -42,14 +54,22 @@ public function run(): void ['name' => 'Laragon', 'slug' => 'laragon'], ]); - $framework = Channel::create(['name' => 'Framework', 'slug' => 'framework', 'color' => '#fb70a9']); + $framework = Channel::query()->create([ + 'name' => 'Framework', + 'slug' => 'framework', + 'color' => '#fb70a9', + ]); $framework->items()->createMany([ ['name' => 'Inertia', 'slug' => 'inertia'], ['name' => 'Livewire', 'slug' => 'livewire'], ['name' => 'TailwindCSS', 'slug' => 'tailwindcss'], ]); - $hosting = Channel::create(['name' => 'Hosting', 'slug' => 'hosting', 'color' => '#0080ff']); + $hosting = Channel::query()->create([ + 'name' => 'Hosting', + 'slug' => 'hosting', + 'color' => '#0080ff', + ]); $hosting->items()->createMany([ ['name' => 'Digital Ocean', 'slug' => 'digital-ocean'], ['name' => 'Forge', 'slug' => 'forge'], @@ -59,18 +79,30 @@ public function run(): void ['name' => 'AWS', 'slug' => 'aws'], ]); - $outils = Channel::create(['name' => 'Outils', 'slug' => 'outils', 'color' => '#333333']); - $outils->items()->createMany([ + $tools = Channel::query()->create([ + 'name' => 'Outils', + 'slug' => 'outils', + 'color' => '#333333', + ]); + $tools->items()->createMany([ ['name' => 'Github Actions', 'slug' => 'github-actions'], - ['name' => 'Gitlab', 'slug' => 'gitlab'], + ['name' => 'Gitlab CI', 'slug' => 'gitlab-ci'], ]); - $design = Channel::create(['name' => 'Design', 'slug' => 'design', 'color' => '#6D28D9']); + $design = Channel::query()->create([ + 'name' => 'Design', + 'slug' => 'design', + 'color' => '#6D28D9', + ]); $design->items()->createMany([ ['name' => 'UI/UX', 'slug' => 'ui-ux'], ]); - $divers = Channel::create(['name' => 'Divers', 'slug' => 'divers', 'color' => '#DB2777']); + $divers = Channel::query()->create([ + 'name' => 'Divers', + 'slug' => 'divers', + 'color' => '#DB2777', + ]); $divers->items()->createMany([ ['name' => 'Laravel.cm', 'slug' => 'laravelcm'], ['name' => 'Gaming', 'slug' => 'gaming'], diff --git a/database/seeders/DeveloperPremiumPlanSeeder.php b/database/seeders/DeveloperPremiumPlanSeeder.php index 5aa65ca5..223d58d1 100644 --- a/database/seeders/DeveloperPremiumPlanSeeder.php +++ b/database/seeders/DeveloperPremiumPlanSeeder.php @@ -14,10 +14,10 @@ final class DeveloperPremiumPlanSeeder extends Seeder { public function run(): void { - $rookiePlan = Plan::create([ - 'name' => 'Le Rookie', + $rookiePlan = Plan::query()->create([ + 'name' => 'Builder', 'description' => 'Pour tous ceux qui veulent apprendre et avoir le contenu minimal', - 'type' => PlanType::DEVELOPER->value, + 'type' => PlanType::DEVELOPER, 'price' => 2000, 'signup_fee' => 0, 'invoice_period' => 1, @@ -34,10 +34,10 @@ public function run(): void new Feature(['name' => 'Accès au code source des tutoriels', 'value' => 1, 'sort_order' => 5]), ]); - $proPlan = Plan::create([ - 'name' => 'Le Pro', + $proPlan = Plan::query()->create([ + 'name' => 'Artisan', 'description' => 'Pour les professionnels du métier', - 'type' => PlanType::DEVELOPER->value, + 'type' => PlanType::DEVELOPER, 'price' => 5000, 'signup_fee' => 0, 'invoice_period' => 1, diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 15c79afa..16aad61e 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,16 +1,16 @@ services: laravelcm: build: - context: '.' + context: . dockerfile: Dockerfile args: NODE_VERSION: '22' PHP_VERSION: 8.4 - image: 'laravelcm/php' + image: laravelcm/php extra_hosts: - 'host.docker.internal:host-gateway' healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:${APP_PORT:-8080}/up"] + test: ['CMD', 'curl', '-f', 'http://localhost:${APP_PORT:-8080}/up'] timeout: 30s environment: APP_NAME: ${APP_NAME} @@ -21,10 +21,6 @@ services: APP_DOMAIN: ${APP_DOMAIN} APP_URL: ${APP_URL} ASSET_URL: ${ASSET_URL} - APP_LICENCE_DOMAIN: ${APP_LICENCE_DOMAIN} - APP_LICENCE_API: ${APP_LICENCE_API} - ANALYTICS_PROPERTY_ID: ${ANALYTICS_PROPERTY_ID} - APP_DEMO: ${APP_DEMO} APP_LOCALE: ${APP_LOCALE} APP_FALLBACK_LOCALE: ${APP_FALLBACK_LOCALE} APP_FAKER_LOCALE: ${APP_FAKER_LOCALE} @@ -75,9 +71,6 @@ services: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} ANTHROPIC_API_VERSION: ${ANTHROPIC_API_VERSION} OLLAMA_URL: ${OLLAMA_URL} - GROQ_API_KEY: ${GROQ_API_KEY} - XAI_API_KEY: ${XAI_API_KEY} - GEMINI_API_KEY: ${GEMINI_API_KEY} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY} SCOUT_DRIVER: ${SCOUT_DRIVER} TYPESENSE_HOST: ${TYPESENSE_HOST} @@ -88,7 +81,7 @@ services: STRIPE_VERSION: ${STRIPE_VERSION} NOTCHPAY_PUBLIC_KEY: ${NOTCHPAY_PUBLIC_KEY} NOTCHPAY_SECRET_KEY: ${NOTCHPAY_SECRET_KEY} - OCTANE_SERVER: frankenphp + OCTANE_SERVER: 'frankenphp' CADDY_GLOBAL_OPTIONS: | servers { trusted_proxies static private_ranges @@ -108,11 +101,11 @@ services: networks: - dokploy-network schedule: - image: 'laravelcm/php' - command: ["artisan", "schedule:work"] + image: laravelcm/php + command: ['artisan', 'schedule:work'] stop_signal: SIGTERM healthcheck: - test: ["CMD", "healthcheck-schedule"] + test: ['CMD', 'healthcheck-schedule'] start_period: 10s environment: APP_NAME: ${APP_NAME} @@ -123,10 +116,6 @@ services: APP_DOMAIN: ${APP_DOMAIN} APP_URL: ${APP_URL} ASSET_URL: ${ASSET_URL} - APP_LICENCE_DOMAIN: ${APP_LICENCE_DOMAIN} - APP_LICENCE_API: ${APP_LICENCE_API} - ANALYTICS_PROPERTY_ID: ${ANALYTICS_PROPERTY_ID} - APP_DEMO: ${APP_DEMO} APP_LOCALE: ${APP_LOCALE} APP_FALLBACK_LOCALE: ${APP_FALLBACK_LOCALE} APP_FAKER_LOCALE: ${APP_FAKER_LOCALE} @@ -177,9 +166,6 @@ services: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} ANTHROPIC_API_VERSION: ${ANTHROPIC_API_VERSION} OLLAMA_URL: ${OLLAMA_URL} - GROQ_API_KEY: ${GROQ_API_KEY} - XAI_API_KEY: ${XAI_API_KEY} - GEMINI_API_KEY: ${GEMINI_API_KEY} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY} SCOUT_DRIVER: ${SCOUT_DRIVER} TYPESENSE_HOST: ${TYPESENSE_HOST} @@ -197,11 +183,11 @@ services: depends_on: - laravelcm queue: - image: 'laravelcm/php' - command: ["artisan", "queue:work", "--tries=3", "--queue=default,media"] + image: laravelcm/php + command: ['artisan', 'queue:work', '--tries=3', '--queue=default,media'] stop_signal: SIGTERM healthcheck: - test: ["CMD", "healthcheck-queue"] + test: ['CMD', 'healthcheck-queue'] start_period: 10s environment: APP_NAME: ${APP_NAME} @@ -212,10 +198,6 @@ services: APP_DOMAIN: ${APP_DOMAIN} APP_URL: ${APP_URL} ASSET_URL: ${ASSET_URL} - APP_LICENCE_DOMAIN: ${APP_LICENCE_DOMAIN} - APP_LICENCE_API: ${APP_LICENCE_API} - ANALYTICS_PROPERTY_ID: ${ANALYTICS_PROPERTY_ID} - APP_DEMO: ${APP_DEMO} APP_LOCALE: ${APP_LOCALE} APP_FALLBACK_LOCALE: ${APP_FALLBACK_LOCALE} APP_FAKER_LOCALE: ${APP_FAKER_LOCALE} @@ -266,9 +248,6 @@ services: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} ANTHROPIC_API_VERSION: ${ANTHROPIC_API_VERSION} OLLAMA_URL: ${OLLAMA_URL} - GROQ_API_KEY: ${GROQ_API_KEY} - XAI_API_KEY: ${XAI_API_KEY} - GEMINI_API_KEY: ${GEMINI_API_KEY} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY} SCOUT_DRIVER: ${SCOUT_DRIVER} TYPESENSE_HOST: ${TYPESENSE_HOST} diff --git a/docker-compose.yml b/docker-compose.yml index e4d21100..1190d4b2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,32 +1,32 @@ services: traefik: - image: traefik:v3.5 + image: 'traefik:v3.5' command: - - "--api.dashboard=true" - - "--api.insecure=true" - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=traefik" - - "--entrypoints.web.address=:80" - - "--entrypoints.websecure.address=:443" - - "--serverstransport.insecureskipverify=true" + - '--api.dashboard=true' + - '--api.insecure=true' + - '--providers.docker=true' + - '--providers.docker.exposedbydefault=false' + - '--providers.docker.network=traefik' + - '--entrypoints.web.address=:80' + - '--entrypoints.websecure.address=:443' + - '--serverstransport.insecureskipverify=true' ports: - - "80:80" - - "443:443" - - "8080:8080" # Dashboard Traefik + - '80:80' + - '443:443' + - '8080:8080' volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" - - "traefik-letsencrypt:/letsencrypt" + - '/var/run/docker.sock:/var/run/docker.sock:ro' + - 'traefik-letsencrypt:/letsencrypt' networks: - traefik - sail labels: - - "traefik.enable=true" - - "traefik.http.routers.traefik.rule=Host(`traefik.local`)" - - "traefik.http.services.traefik.loadbalancer.server.port=8080" + - traefik.enable=true + - traefik.http.routers.traefik.rule=Host(`traefik.local`) + - traefik.http.services.traefik.loadbalancer.server.port=8080 laravelcm: build: - context: '.' + context: . dockerfile: Dockerfile target: development args: @@ -34,7 +34,7 @@ services: WWWGROUP: '${WWWGROUP}' NODE_VERSION: '22' PHP_VERSION: 8.4 - image: 'laravelcm/php' + image: laravelcm/php extra_hosts: - 'host.docker.internal:host-gateway' ports: @@ -48,7 +48,7 @@ services: XDG_CONFIG_HOME: /var/www/html/config XDG_DATA_HOME: /var/www/html/data healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:${APP_PORT:-8080}/up" ] + test: ['CMD', 'curl', '-f', 'http://localhost:${APP_PORT:-8080}/up'] timeout: 30s volumes: - '.:/var/www/html' @@ -56,7 +56,7 @@ services: - sail - traefik depends_on: - - mysql + - pgsql - redis - typesense - minio @@ -64,22 +64,21 @@ services: - soketi - traefik labels: - - "traefik.enable=true" - - "traefik.http.routers.laravelcm.rule=Host(`${APP_DOMAIN:-laravelcm.local}`)" - - "traefik.http.routers.laravelcm.entrypoints=websecure" - - "traefik.http.routers.laravelcm.tls=true" - - "traefik.http.services.laravelcm.loadbalancer.server.port=${APP_PORT:-8080}" - # Redirection HTTP vers HTTPS - - "traefik.http.routers.laravelcm-insecure.rule=Host(`${APP_DOMAIN:-laravelcm.local}`)" - - "traefik.http.routers.laravelcm-insecure.entrypoints=web" - - "traefik.http.routers.laravelcm-insecure.middlewares=redirect-to-https" - - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" + - traefik.enable=true + - 'traefik.http.routers.laravelcm.rule=Host(`${APP_DOMAIN:-laravelcm.local}`)' + - traefik.http.routers.laravelcm.entrypoints=websecure + - traefik.http.routers.laravelcm.tls=true + - 'traefik.http.services.laravelcm.loadbalancer.server.port=${APP_PORT:-8080}' + - 'traefik.http.routers.laravelcm-insecure.rule=Host(`${APP_DOMAIN:-laravelcm.local}`)' + - traefik.http.routers.laravelcm-insecure.entrypoints=web + - traefik.http.routers.laravelcm-insecure.middlewares=redirect-to-https + - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https schedule: - image: 'laravelcm/php' - command: ["artisan", "schedule:work"] + image: laravelcm/php + command: ['artisan', 'schedule:work'] stop_signal: SIGTERM healthcheck: - test: ["CMD", "healthcheck-schedule"] + test: ['CMD', 'healthcheck-schedule'] start_period: 10s volumes: - '.:/var/www/html' @@ -87,14 +86,18 @@ services: - sail depends_on: - laravelcm - - mysql + - pgsql - redis queue: - image: 'laravelcm/php' - command: ["artisan", "queue:work", "--tries=3", "--queue=default,media"] + image: laravelcm/php + command: + - artisan + - 'queue:work' + - '--tries=3' + - '--queue=default,media' stop_signal: SIGTERM healthcheck: - test: ["CMD", "healthcheck-queue"] + test: ['CMD', 'healthcheck-queue'] start_period: 10s volumes: - '.:/var/www/html' @@ -102,31 +105,31 @@ services: - sail depends_on: - laravelcm - - mysql + - pgsql - redis - mysql: - image: 'mysql:8.0' + pgsql: + image: 'postgres:17-alpine' ports: - - '${FORWARD_DB_PORT:-3306}:3306' + - '${FORWARD_DB_PORT:-5432}:5432' environment: - MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' - MYSQL_ROOT_HOST: '%' - MYSQL_DATABASE: '${DB_DATABASE}' - MYSQL_USER: '${DB_USERNAME}' - MYSQL_PASSWORD: '${DB_PASSWORD}' - MYSQL_ALLOW_EMPTY_PASSWORD: 1 - command: --default-authentication-plugin=mysql_native_password + PGPASSWORD: '${DB_PASSWORD:-secret}' + POSTGRES_DB: '${DB_DATABASE}' + POSTGRES_USER: '${DB_USERNAME}' + POSTGRES_PASSWORD: '${DB_PASSWORD:-secret}' volumes: - - 'sail-mysql:/var/lib/mysql' - - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' + - 'sail-pgsql:/var/lib/postgresql/data' + - './vendor/laravel/sail/database/pgsql/create-testing-database.sql:/docker-entrypoint-initdb.d/10-create-testing-database.sql' networks: - sail healthcheck: test: - CMD - - mysqladmin - - ping - - '-p${DB_PASSWORD}' + - pg_isready + - '-q' + - '-d' + - '${DB_DATABASE}' + - '-U' + - '${DB_USERNAME}' retries: 3 timeout: 5s redis: @@ -211,8 +214,6 @@ networks: traefik: driver: bridge volumes: - sail-mysql: - driver: local sail-redis: driver: local sail-typesense: @@ -221,3 +222,5 @@ volumes: driver: local traefik-letsencrypt: driver: local + sail-pgsql: + driver: local