refactor: remove all Docker configuration and orchestration files
This commit is contained in:
parent
d8149ef058
commit
9f9ecf400e
14 changed files with 0 additions and 1212 deletions
|
|
@ -1,78 +0,0 @@
|
||||||
# Version control & editors
|
|
||||||
.git
|
|
||||||
.gitattributes
|
|
||||||
.gitignore
|
|
||||||
.github
|
|
||||||
.editorconfig
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
.fleet
|
|
||||||
.nova
|
|
||||||
.zed
|
|
||||||
.phpactor.json
|
|
||||||
|
|
||||||
# Dependencies (installed inside the image)
|
|
||||||
vendor
|
|
||||||
node_modules
|
|
||||||
maizzle/node_modules
|
|
||||||
|
|
||||||
# Build artifacts (generated inside the image)
|
|
||||||
public/build
|
|
||||||
public/hot
|
|
||||||
public/storage
|
|
||||||
bootstrap/ssr
|
|
||||||
bootstrap/cache/*.php
|
|
||||||
|
|
||||||
# Generated route/action helpers (regenerated by Wayfinder at build time)
|
|
||||||
resources/js/actions
|
|
||||||
resources/js/routes
|
|
||||||
resources/js/wayfinder
|
|
||||||
|
|
||||||
# Runtime storage (mounted at runtime; shouldn't bloat build context)
|
|
||||||
storage/logs
|
|
||||||
storage/framework/cache/data
|
|
||||||
storage/framework/sessions
|
|
||||||
storage/framework/views
|
|
||||||
storage/pail
|
|
||||||
storage/*.key
|
|
||||||
storage/oauth-*.key
|
|
||||||
|
|
||||||
# Test caches
|
|
||||||
.phpunit.cache
|
|
||||||
.phpunit.result.cache
|
|
||||||
|
|
||||||
# Environment files (image must not embed secrets)
|
|
||||||
.env
|
|
||||||
.env.backup
|
|
||||||
.env.local
|
|
||||||
.env.production
|
|
||||||
.env.testing
|
|
||||||
|
|
||||||
# Composer auth
|
|
||||||
auth.json
|
|
||||||
|
|
||||||
# Personal homelab/deploy artifacts (not part of upstream)
|
|
||||||
deploy
|
|
||||||
HOMELAB-DEPLOY-GUIDE.md
|
|
||||||
|
|
||||||
# Compose overrides (per-developer)
|
|
||||||
compose.override.yaml
|
|
||||||
compose.override.yml
|
|
||||||
docker-compose.override.yaml
|
|
||||||
docker-compose.override.yml
|
|
||||||
|
|
||||||
# Docs site (if present)
|
|
||||||
docs
|
|
||||||
|
|
||||||
# OS / misc
|
|
||||||
.DS_Store
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Plan / agent context (not relevant for build)
|
|
||||||
.claude
|
|
||||||
.mcp.json
|
|
||||||
CLAUDE.md
|
|
||||||
GEMINI.md
|
|
||||||
boost.json
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
# TryPost — Compose override example.
|
|
||||||
#
|
|
||||||
# Compose automatically merges `compose.override.yaml` (gitignored, per-developer)
|
|
||||||
# on top of `compose.yaml`. Copy this file to start customizing:
|
|
||||||
#
|
|
||||||
# cp compose.override.yaml.example compose.override.yaml
|
|
||||||
#
|
|
||||||
# Common overrides are commented below. Uncomment what you need.
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
# Match container UID/GID to your host user (Linux contributors on
|
|
||||||
# systems where your UID isn't 1000).
|
|
||||||
# build:
|
|
||||||
# args:
|
|
||||||
# UID: 1001
|
|
||||||
# GID: 1001
|
|
||||||
|
|
||||||
# Enable Xdebug (requires installing the extension via a Dockerfile
|
|
||||||
# tweak; left commented as a hint).
|
|
||||||
# environment:
|
|
||||||
# XDEBUG_MODE: debug
|
|
||||||
# XDEBUG_CONFIG: 'client_host=host.docker.internal'
|
|
||||||
|
|
||||||
# Forward additional ports if 8000/5173/8080 conflict with another
|
|
||||||
# service on your host.
|
|
||||||
# ports:
|
|
||||||
# - '9000:80'
|
|
||||||
|
|
||||||
pgsql:
|
|
||||||
# Free up host port 5432 (e.g. when running another Postgres locally).
|
|
||||||
# ports:
|
|
||||||
# - '54320:5432'
|
|
||||||
|
|
||||||
redis:
|
|
||||||
# ports:
|
|
||||||
# - '63790:6379'
|
|
||||||
|
|
||||||
mailpit:
|
|
||||||
# Move the Mailpit UI off port 8025.
|
|
||||||
# ports:
|
|
||||||
# - '8025:8025'
|
|
||||||
# - '1025:1025'
|
|
||||||
|
|
||||||
# Add additional services here — e.g. a Selenium container for Dusk tests:
|
|
||||||
#
|
|
||||||
# selenium:
|
|
||||||
# image: selenium/standalone-chromium
|
|
||||||
# extra_hosts:
|
|
||||||
# - 'host.docker.internal:host-gateway'
|
|
||||||
# volumes:
|
|
||||||
# - '/dev/shm:/dev/shm'
|
|
||||||
|
|
@ -1,189 +0,0 @@
|
||||||
# TryPost — self-hosted production stack.
|
|
||||||
#
|
|
||||||
# 1. Generate an app key: docker compose -f compose.prod.yaml run --rm app php artisan key:generate --show
|
|
||||||
# Paste the value into APP_KEY below.
|
|
||||||
# 2. Edit APP_URL and the passwords marked "change me".
|
|
||||||
# 3. Start: docker compose -f compose.prod.yaml up -d
|
|
||||||
#
|
|
||||||
# This pulls the published image — no local build. Postgres, Redis, the queue
|
|
||||||
# workers, the scheduler and the WebSocket server all run for you.
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: ghcr.io/trypostit/trypost:latest
|
|
||||||
container_name: trypost
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
# ===== Required =====
|
|
||||||
APP_NAME: TryPost
|
|
||||||
APP_ENV: production
|
|
||||||
APP_DEBUG: "false"
|
|
||||||
APP_KEY: "" # <- run key:generate (see header) and paste here
|
|
||||||
APP_URL: http://localhost:8000 # <- your public URL, e.g. https://post.yourdomain.com
|
|
||||||
SELF_HOSTED: "true"
|
|
||||||
ALLOW_MULTIPLE_SOCIAL_ACCOUNTS: "true"
|
|
||||||
TRYPOST_TARGET: production
|
|
||||||
|
|
||||||
# ===== Database (bundled postgres service below) =====
|
|
||||||
DB_CONNECTION: pgsql
|
|
||||||
DB_HOST: pgsql
|
|
||||||
DB_PORT: "5432"
|
|
||||||
DB_DATABASE: trypost
|
|
||||||
DB_USERNAME: trypost
|
|
||||||
DB_PASSWORD: trypost-password # <- change me (must match POSTGRES_PASSWORD below)
|
|
||||||
|
|
||||||
# ===== Redis / queue / cache / broadcasting =====
|
|
||||||
REDIS_HOST: redis
|
|
||||||
REDIS_PORT: "6379"
|
|
||||||
QUEUE_CONNECTION: redis
|
|
||||||
CACHE_STORE: redis
|
|
||||||
SESSION_DRIVER: database
|
|
||||||
BROADCAST_CONNECTION: reverb
|
|
||||||
|
|
||||||
# ===== WebSockets (Reverb) =====
|
|
||||||
# The published image bakes the client at localhost:8080. For a custom
|
|
||||||
# domain, rebuild the image with --build-arg VITE_REVERB_HOST=<domain>
|
|
||||||
# VITE_REVERB_PORT=443 VITE_REVERB_SCHEME=https.
|
|
||||||
REVERB_APP_ID: "1001"
|
|
||||||
REVERB_APP_KEY: trypost-reverb-key # must match the key baked into the published image
|
|
||||||
REVERB_APP_SECRET: change-me-reverb-secret # <- change me
|
|
||||||
REVERB_HOST: localhost
|
|
||||||
REVERB_PORT: "8080"
|
|
||||||
REVERB_SCHEME: http
|
|
||||||
|
|
||||||
# ===== Passport (OAuth / API keys / MCP) =====
|
|
||||||
# REQUIRED for any durable deploy (and always for multi-node / load
|
|
||||||
# balancers). File keys under storage/ are NOT persisted by the volumes
|
|
||||||
# below — without these env vars a container recreate issues new keys and
|
|
||||||
# invalidates every API/MCP token. Generate once:
|
|
||||||
# docker compose -f compose.prod.yaml run --rm app php artisan passport:keys --show
|
|
||||||
# then paste the PEM contents here (use \n for newlines).
|
|
||||||
PASSPORT_PRIVATE_KEY: ""
|
|
||||||
PASSPORT_PUBLIC_KEY: ""
|
|
||||||
|
|
||||||
# ===== Storage =====
|
|
||||||
# Default: local disk, persisted in the "storage" volume below.
|
|
||||||
FILESYSTEM_DISK: public
|
|
||||||
#
|
|
||||||
# --- Cloudflare R2: set FILESYSTEM_DISK=r2 and fill these ---
|
|
||||||
# R2_ACCESS_KEY_ID: ""
|
|
||||||
# R2_SECRET_ACCESS_KEY: ""
|
|
||||||
# R2_ENDPOINT: ""
|
|
||||||
# R2_BUCKET: ""
|
|
||||||
# R2_URL: "" # public bucket URL (needed for media to display)
|
|
||||||
#
|
|
||||||
# --- AWS S3: set FILESYSTEM_DISK=s3 and fill these ---
|
|
||||||
# AWS_ACCESS_KEY_ID: ""
|
|
||||||
# AWS_SECRET_ACCESS_KEY: ""
|
|
||||||
# AWS_DEFAULT_REGION: us-east-1
|
|
||||||
# AWS_BUCKET: ""
|
|
||||||
# AWS_URL: ""
|
|
||||||
|
|
||||||
# ===== Mail =====
|
|
||||||
# Defaults to "log" (emails written to the container log). Configure SMTP
|
|
||||||
# for real password-reset / team-invite emails.
|
|
||||||
MAIL_MAILER: log
|
|
||||||
MAIL_FROM_ADDRESS: hello@example.com
|
|
||||||
MAIL_FROM_NAME: TryPost
|
|
||||||
# MAIL_MAILER: smtp
|
|
||||||
# MAIL_HOST: ""
|
|
||||||
# MAIL_PORT: "587"
|
|
||||||
# MAIL_USERNAME: ""
|
|
||||||
# MAIL_PASSWORD: ""
|
|
||||||
# MAIL_SCHEME: tls
|
|
||||||
|
|
||||||
# ===== Social platforms (fill in when you connect each network) =====
|
|
||||||
# Redirect URI in each portal: ${APP_URL}/accounts/<platform>/callback
|
|
||||||
# LINKEDIN_CLIENT_ID: ""
|
|
||||||
# LINKEDIN_CLIENT_SECRET: ""
|
|
||||||
# X_CLIENT_ID: ""
|
|
||||||
# X_CLIENT_SECRET: ""
|
|
||||||
# FACEBOOK_CLIENT_ID: ""
|
|
||||||
# FACEBOOK_CLIENT_SECRET: ""
|
|
||||||
# INSTAGRAM_CLIENT_ID: ""
|
|
||||||
# INSTAGRAM_CLIENT_SECRET: ""
|
|
||||||
# THREADS_CLIENT_ID: ""
|
|
||||||
# THREADS_CLIENT_SECRET: ""
|
|
||||||
# TIKTOK_CLIENT_ID: ""
|
|
||||||
# TIKTOK_CLIENT_SECRET: ""
|
|
||||||
# PINTEREST_CLIENT_ID: ""
|
|
||||||
# PINTEREST_CLIENT_SECRET: ""
|
|
||||||
# GOOGLE_CLIENT_ID: "" # YouTube + Google login
|
|
||||||
# GOOGLE_CLIENT_SECRET: ""
|
|
||||||
|
|
||||||
# ===== AI (optional — leave blank to disable AI features) =====
|
|
||||||
# OPENAI_API_KEY: ""
|
|
||||||
# ANTHROPIC_API_KEY: ""
|
|
||||||
# GEMINI_API_KEY: ""
|
|
||||||
# OPENROUTER_API_KEY: ""
|
|
||||||
# ELEVENLABS_API_KEY: ""
|
|
||||||
ports:
|
|
||||||
- "8000:80" # app (nginx)
|
|
||||||
- "8080:8080" # Reverb WebSocket
|
|
||||||
volumes:
|
|
||||||
- storage:/var/www/html/storage/app
|
|
||||||
depends_on:
|
|
||||||
pgsql:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
pgsql:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
container_name: trypost-pgsql
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: trypost
|
|
||||||
POSTGRES_USER: trypost
|
|
||||||
POSTGRES_PASSWORD: trypost-password # <- must match DB_PASSWORD above
|
|
||||||
volumes:
|
|
||||||
- pgdata:/var/lib/postgresql/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD-SHELL', 'pg_isready -U trypost -d trypost']
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
container_name: trypost-redis
|
|
||||||
restart: unless-stopped
|
|
||||||
command: redis-server --appendonly yes
|
|
||||||
volumes:
|
|
||||||
- redisdata:/data
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'redis-cli', 'ping']
|
|
||||||
interval: 10s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
# Optional reverse proxy with automatic HTTPS (Let's Encrypt).
|
|
||||||
# To serve on a domain:
|
|
||||||
# 1. Point the domain's DNS at this host.
|
|
||||||
# 2. Set APP_DOMAIN below and APP_URL above to https://<that domain>.
|
|
||||||
# 3. Start with the proxy profile:
|
|
||||||
# docker compose -f compose.prod.yaml --profile proxy up -d
|
|
||||||
# Without the profile, the app is served directly on http://localhost:8000.
|
|
||||||
caddy:
|
|
||||||
image: caddy:2-alpine
|
|
||||||
container_name: trypost-caddy
|
|
||||||
restart: unless-stopped
|
|
||||||
profiles: [proxy]
|
|
||||||
environment:
|
|
||||||
APP_DOMAIN: post.example.com # <- your domain
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
volumes:
|
|
||||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
||||||
- caddy-data:/data
|
|
||||||
- caddy-config:/config
|
|
||||||
depends_on:
|
|
||||||
- app
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
||||||
redisdata:
|
|
||||||
storage:
|
|
||||||
caddy-data:
|
|
||||||
caddy-config:
|
|
||||||
83
compose.yaml
83
compose.yaml
|
|
@ -1,83 +0,0 @@
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/Dockerfile
|
|
||||||
target: dev
|
|
||||||
args:
|
|
||||||
UID: ${UID:-1000}
|
|
||||||
GID: ${GID:-1000}
|
|
||||||
image: trypost-app:dev
|
|
||||||
ports:
|
|
||||||
- '${APP_PORT:-8000}:80'
|
|
||||||
- '${REVERB_PORT:-8080}:8080'
|
|
||||||
- '${VITE_PORT:-5173}:5173'
|
|
||||||
environment:
|
|
||||||
TRYPOST_DOCKER_BOOTSTRAP: '1'
|
|
||||||
UID: ${UID:-1000}
|
|
||||||
GID: ${GID:-1000}
|
|
||||||
# Real env vars take precedence over .env.testing (which pins
|
|
||||||
# DB_HOST=127.0.0.1, DB_USERNAME=root). Without these, the test
|
|
||||||
# suite can't see Postgres / Redis from inside the container.
|
|
||||||
DB_HOST: pgsql
|
|
||||||
DB_USERNAME: postgres
|
|
||||||
REDIS_HOST: redis
|
|
||||||
volumes:
|
|
||||||
- .:/var/www/html
|
|
||||||
- app-vendor:/var/www/html/vendor
|
|
||||||
- app-node-modules:/var/www/html/node_modules
|
|
||||||
extra_hosts:
|
|
||||||
- 'host.docker.internal:host-gateway'
|
|
||||||
depends_on:
|
|
||||||
pgsql:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'curl', '-fsS', 'http://127.0.0.1/up']
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 90s
|
|
||||||
|
|
||||||
pgsql:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: ${DB_DATABASE:-trypost}
|
|
||||||
POSTGRES_USER: ${DB_USERNAME:-postgres}
|
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-password}
|
|
||||||
volumes:
|
|
||||||
- pgdata:/var/lib/postgresql/data
|
|
||||||
- ./docker/postgres-init.sh:/docker-entrypoint-initdb.d/10-create-test-db.sh:ro
|
|
||||||
ports:
|
|
||||||
- '${FORWARD_DB_PORT:-5432}:5432'
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD-SHELL', 'pg_isready -U ${DB_USERNAME:-postgres} -d ${DB_DATABASE:-trypost}']
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
command: redis-server --appendonly yes
|
|
||||||
volumes:
|
|
||||||
- redisdata:/data
|
|
||||||
ports:
|
|
||||||
- '${FORWARD_REDIS_PORT:-6379}:6379'
|
|
||||||
healthcheck:
|
|
||||||
test: ['CMD', 'redis-cli', 'ping']
|
|
||||||
interval: 10s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
mailpit:
|
|
||||||
image: axllent/mailpit:latest
|
|
||||||
ports:
|
|
||||||
- '${FORWARD_MAILPIT_SMTP_PORT:-1025}:1025'
|
|
||||||
- '${FORWARD_MAILPIT_UI_PORT:-8025}:8025'
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
pgdata:
|
|
||||||
redisdata:
|
|
||||||
app-vendor:
|
|
||||||
app-node-modules:
|
|
||||||
|
|
@ -68,7 +68,6 @@
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
"laravel/pail": "^1.2.2",
|
"laravel/pail": "^1.2.2",
|
||||||
"laravel/pint": "^1.24",
|
"laravel/pint": "^1.24",
|
||||||
"laravel/sail": "*",
|
|
||||||
"laravel/telescope": "^5.19",
|
"laravel/telescope": "^5.19",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.6",
|
"nunomaduro/collision": "^8.6",
|
||||||
|
|
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
APP_NAME="TryPost"
|
|
||||||
APP_ENV=local
|
|
||||||
APP_KEY=
|
|
||||||
APP_DEBUG=true
|
|
||||||
APP_URL=http://localhost:8000
|
|
||||||
|
|
||||||
# Public base URL inbound webhooks (e.g. Telegram) are registered on.
|
|
||||||
# Defaults to APP_URL; set a tunnel URL (e.g. ngrok) for local development.
|
|
||||||
WEBHOOK_URL=
|
|
||||||
|
|
||||||
# Self-hosted mode (skips payment requirements)
|
|
||||||
SELF_HOSTED=true
|
|
||||||
|
|
||||||
# Allow more than one connected account per social network in a workspace.
|
|
||||||
ALLOW_MULTIPLE_SOCIAL_ACCOUNTS=true
|
|
||||||
|
|
||||||
TELESCOPE_ENABLED=false
|
|
||||||
|
|
||||||
APP_LOCALE=en
|
|
||||||
APP_FALLBACK_LOCALE=en
|
|
||||||
APP_FAKER_LOCALE=en_US
|
|
||||||
|
|
||||||
APP_MAINTENANCE_DRIVER=file
|
|
||||||
|
|
||||||
BCRYPT_ROUNDS=12
|
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
|
||||||
LOG_STACK=single
|
|
||||||
LOG_DEPRECATIONS_CHANNEL=null
|
|
||||||
LOG_LEVEL=debug
|
|
||||||
|
|
||||||
# Database — service name "pgsql" inside the Compose network.
|
|
||||||
DB_CONNECTION=pgsql
|
|
||||||
DB_HOST=pgsql
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_DATABASE=trypost
|
|
||||||
DB_USERNAME=postgres
|
|
||||||
DB_PASSWORD=password
|
|
||||||
|
|
||||||
# Session
|
|
||||||
SESSION_DRIVER=database
|
|
||||||
SESSION_LIFETIME=1440
|
|
||||||
SESSION_ENCRYPT=false
|
|
||||||
SESSION_PATH=/
|
|
||||||
SESSION_DOMAIN=null
|
|
||||||
|
|
||||||
# Broadcasting, Queue, Cache — all Redis-backed in Docker.
|
|
||||||
BROADCAST_CONNECTION=reverb
|
|
||||||
QUEUE_CONNECTION=redis
|
|
||||||
CACHE_STORE=redis
|
|
||||||
|
|
||||||
# File Storage — local-disk by default; switch to s3/r2 for production.
|
|
||||||
FILESYSTEM_DISK=local
|
|
||||||
|
|
||||||
# Redis — service name "redis" inside the Compose network.
|
|
||||||
REDIS_HOST=redis
|
|
||||||
REDIS_PASSWORD=null
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
# Mail — service name "mailpit" inside the Compose network. UI at :8025.
|
|
||||||
MAIL_MAILER=smtp
|
|
||||||
MAIL_HOST=mailpit
|
|
||||||
MAIL_PORT=1025
|
|
||||||
MAIL_USERNAME=null
|
|
||||||
MAIL_PASSWORD=null
|
|
||||||
MAIL_ENCRYPTION=null
|
|
||||||
MAIL_FROM_ADDRESS="hello@example.com"
|
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
|
||||||
|
|
||||||
# Reverb (WebSockets) — host=localhost is browser-perspective via host port-forward.
|
|
||||||
REVERB_APP_ID=1001
|
|
||||||
REVERB_APP_KEY=trypost-reverb-key
|
|
||||||
REVERB_APP_SECRET=trypost-reverb-secret
|
|
||||||
REVERB_HOST=localhost
|
|
||||||
REVERB_PORT=8080
|
|
||||||
REVERB_SCHEME=http
|
|
||||||
|
|
||||||
# AWS S3 (set FILESYSTEM_DISK=s3)
|
|
||||||
AWS_ACCESS_KEY_ID=
|
|
||||||
AWS_SECRET_ACCESS_KEY=
|
|
||||||
AWS_DEFAULT_REGION=us-east-1
|
|
||||||
AWS_BUCKET=
|
|
||||||
AWS_URL=
|
|
||||||
|
|
||||||
# Cloudflare R2 (set FILESYSTEM_DISK=r2)
|
|
||||||
R2_ACCESS_KEY_ID=
|
|
||||||
R2_SECRET_ACCESS_KEY=
|
|
||||||
R2_ENDPOINT=
|
|
||||||
R2_REGION=auto
|
|
||||||
R2_BUCKET=
|
|
||||||
R2_URL=
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Social Platform Credentials (optional in dev)
|
|
||||||
# ============================================
|
|
||||||
LINKEDIN_CLIENT_ID=
|
|
||||||
LINKEDIN_CLIENT_SECRET=
|
|
||||||
LINKEDIN_CLIENT_REDIRECT="${APP_URL}/accounts/linkedin/callback"
|
|
||||||
# LINKEDIN_SCOPES="openid,profile,email,w_member_social"
|
|
||||||
LINKEDIN_PAGE_CLIENT_REDIRECT="${APP_URL}/accounts/linkedin-page/callback"
|
|
||||||
# LINKEDIN_PAGE_SCOPES="openid,profile,email,w_organization_social,r_organization_social,rw_organization_admin,w_member_social"
|
|
||||||
|
|
||||||
X_CLIENT_ID=
|
|
||||||
X_CLIENT_SECRET=
|
|
||||||
X_CLIENT_REDIRECT="${APP_URL}/accounts/x/callback"
|
|
||||||
|
|
||||||
TIKTOK_CLIENT_ID=
|
|
||||||
TIKTOK_CLIENT_SECRET=
|
|
||||||
TIKTOK_CLIENT_REDIRECT="${APP_URL}/accounts/tiktok/callback"
|
|
||||||
|
|
||||||
FACEBOOK_CLIENT_ID=
|
|
||||||
FACEBOOK_CLIENT_SECRET=
|
|
||||||
FACEBOOK_CLIENT_REDIRECT="${APP_URL}/accounts/facebook/callback"
|
|
||||||
|
|
||||||
INSTAGRAM_CLIENT_ID=
|
|
||||||
INSTAGRAM_CLIENT_SECRET=
|
|
||||||
INSTAGRAM_CLIENT_REDIRECT="${APP_URL}/accounts/instagram/callback"
|
|
||||||
|
|
||||||
THREADS_CLIENT_ID=
|
|
||||||
THREADS_CLIENT_SECRET=
|
|
||||||
THREADS_CLIENT_REDIRECT="${APP_URL}/accounts/threads/callback"
|
|
||||||
|
|
||||||
GOOGLE_AUTH_ENABLED=false
|
|
||||||
GOOGLE_CLIENT_ID=
|
|
||||||
GOOGLE_CLIENT_SECRET=
|
|
||||||
GOOGLE_CLIENT_REDIRECT="${APP_URL}/accounts/youtube/callback"
|
|
||||||
GOOGLE_AUTH_CALLBACK="${APP_URL}/auth/google/callback"
|
|
||||||
|
|
||||||
GITHUB_AUTH_ENABLED=false
|
|
||||||
GITHUB_CLIENT_ID=
|
|
||||||
GITHUB_CLIENT_SECRET=
|
|
||||||
GITHUB_AUTH_CALLBACK="${APP_URL}/auth/github/callback"
|
|
||||||
|
|
||||||
PINTEREST_CLIENT_ID=
|
|
||||||
PINTEREST_CLIENT_SECRET=
|
|
||||||
PINTEREST_CLIENT_REDIRECT="${APP_URL}/accounts/pinterest/callback"
|
|
||||||
|
|
||||||
# Telegram (single shared bot — create one via https://t.me/BotFather)
|
|
||||||
# After setting these, run: php artisan telegram:set-webhook
|
|
||||||
TELEGRAM_BOT_TOKEN=
|
|
||||||
TELEGRAM_BOT_USERNAME=
|
|
||||||
TELEGRAM_WEBHOOK_SECRET=
|
|
||||||
|
|
||||||
# AI Services
|
|
||||||
OPENAI_API_KEY=
|
|
||||||
ANTHROPIC_API_KEY=
|
|
||||||
GEMINI_API_KEY=
|
|
||||||
OPENROUTER_API_KEY=
|
|
||||||
ELEVENLABS_API_KEY=
|
|
||||||
# Only needed if you point one of the AI_*_PROVIDER vars below at these providers.
|
|
||||||
# XAI_API_KEY=
|
|
||||||
# GROQ_API_KEY=
|
|
||||||
# MISTRAL_API_KEY=
|
|
||||||
# DEEPSEEK_API_KEY=
|
|
||||||
# Ollama runs locally and needs no key — from inside Docker point it at the host, not localhost.
|
|
||||||
# OLLAMA_URL=http://host.docker.internal:11434
|
|
||||||
# Any OpenAI-compatible endpoint (LM Studio, vLLM, LocalAI, ...) via AI_TEXT_PROVIDER=openai-compatible.
|
|
||||||
# It has no built-in default model, so OPENAI_COMPATIBLE_TEXT_MODEL is required for that provider.
|
|
||||||
# OPENAI_COMPATIBLE_URL=
|
|
||||||
# OPENAI_COMPATIBLE_API_KEY=
|
|
||||||
# OPENAI_COMPATIBLE_TEXT_MODEL=
|
|
||||||
|
|
||||||
# AI Provider Selection
|
|
||||||
# text: openai | anthropic | gemini | openrouter | xai | groq | mistral | deepseek | ollama | ...
|
|
||||||
# image: openai | gemini | xai | openrouter | ...
|
|
||||||
# audio: openai | elevenlabs | gemini | openrouter | ...
|
|
||||||
# OpenRouter is a first-class laravel/ai provider (AI_TEXT_PROVIDER=openrouter + OPENROUTER_API_KEY).
|
|
||||||
AI_TEXT_PROVIDER=openai
|
|
||||||
AI_IMAGE_PROVIDER=openai
|
|
||||||
AI_AUDIO_PROVIDER=elevenlabs
|
|
||||||
|
|
||||||
# AI Model Overrides (optional)
|
|
||||||
# Each provider picks a sensible default model per capability on its own -
|
|
||||||
# these are NOT shared across capabilities, so overriding one (e.g. a text
|
|
||||||
# model) never affects another (e.g. that same provider's image model).
|
|
||||||
# Uncomment only the ones you want to override; every provider in play above
|
|
||||||
# (AI_TEXT_PROVIDER / AI_IMAGE_PROVIDER / AI_AUDIO_PROVIDER) reads its own set.
|
|
||||||
# The values below are laravel/ai's current defaults, shown as format examples -
|
|
||||||
# they move with the package, so don't treat them as a contract.
|
|
||||||
# OPENAI_TEXT_MODEL=gpt-5.4
|
|
||||||
# OPENAI_IMAGE_MODEL=gpt-image-2
|
|
||||||
# OPENAI_AUDIO_MODEL=gpt-4o-mini-tts
|
|
||||||
# ANTHROPIC_TEXT_MODEL=claude-sonnet-5
|
|
||||||
# GEMINI_TEXT_MODEL=gemini-3.6-flash
|
|
||||||
# GEMINI_IMAGE_MODEL=gemini-3.1-flash-image-preview
|
|
||||||
# GEMINI_AUDIO_MODEL=gemini-2.5-flash-preview-tts
|
|
||||||
# XAI_TEXT_MODEL=grok-4.20-non-reasoning
|
|
||||||
# XAI_IMAGE_MODEL=grok-imagine-image
|
|
||||||
# OPENROUTER_TEXT_MODEL=anthropic/claude-sonnet-4.6
|
|
||||||
# OPENROUTER_IMAGE_MODEL=google/gemini-3.1-flash-image-preview
|
|
||||||
# OPENROUTER_AUDIO_MODEL=google/gemini-3.1-flash-tts-preview
|
|
||||||
# ELEVENLABS_AUDIO_MODEL=eleven_multilingual_v2
|
|
||||||
# GROQ_TEXT_MODEL=openai/gpt-oss-120b
|
|
||||||
# MISTRAL_TEXT_MODEL=mistral-medium-latest
|
|
||||||
# DEEPSEEK_TEXT_MODEL=deepseek-v4-flash
|
|
||||||
# OLLAMA_TEXT_MODEL=llama3.1:8b
|
|
||||||
# Ollama has no image/audio-capable driver in laravel/ai - text only.
|
|
||||||
|
|
||||||
# Telemetry — disabled by default in Docker dev.
|
|
||||||
NIGHTWATCH_ENABLED=false
|
|
||||||
NIGHTWATCH_TOKEN=
|
|
||||||
|
|
||||||
# Media Services
|
|
||||||
UNSPLASH_ACCESS_KEY=
|
|
||||||
UNSPLASH_SECRET_KEY=
|
|
||||||
GIPHY_API_KEY=
|
|
||||||
|
|
||||||
# Analytics (optional)
|
|
||||||
GTM_ID=
|
|
||||||
POSTHOG_ENABLED=false
|
|
||||||
POSTHOG_API_KEY=
|
|
||||||
POSTHOG_HOST=https://us.i.posthog.com
|
|
||||||
|
|
||||||
# Vite
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
|
||||||
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
|
||||||
VITE_REVERB_HOST="${REVERB_HOST}"
|
|
||||||
VITE_REVERB_PORT="${REVERB_PORT}"
|
|
||||||
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
|
||||||
VITE_POSTHOG_ENABLED="${POSTHOG_ENABLED}"
|
|
||||||
VITE_POSTHOG_API_KEY="${POSTHOG_API_KEY}"
|
|
||||||
VITE_POSTHOG_HOST="${POSTHOG_HOST}"
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
# syntax=docker/dockerfile:1.7
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# TryPost Dockerfile — multi-stage with `dev` and `production` targets.
|
|
||||||
#
|
|
||||||
# docker build --target dev -t trypost:dev -f docker/Dockerfile .
|
|
||||||
# docker build --target production -t trypost:prod -f docker/Dockerfile .
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
ARG PHP_VERSION=8.4
|
|
||||||
ARG NODE_VERSION=22
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Stage 1: system-base — PHP-FPM + system packages + extensions
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
FROM php:${PHP_VERSION}-fpm-alpine AS system-base
|
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
nginx \
|
|
||||||
supervisor \
|
|
||||||
bash \
|
|
||||||
curl \
|
|
||||||
git \
|
|
||||||
unzip \
|
|
||||||
shadow \
|
|
||||||
tzdata \
|
|
||||||
postgresql-client \
|
|
||||||
postgresql-dev \
|
|
||||||
mysql-client \
|
|
||||||
libpng-dev \
|
|
||||||
libjpeg-turbo-dev \
|
|
||||||
freetype-dev \
|
|
||||||
libwebp-dev \
|
|
||||||
libavif-dev \
|
|
||||||
libzip-dev \
|
|
||||||
oniguruma-dev \
|
|
||||||
icu-dev \
|
|
||||||
linux-headers \
|
|
||||||
$PHPIZE_DEPS \
|
|
||||||
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp --with-avif \
|
|
||||||
&& docker-php-ext-install -j"$(nproc)" \
|
|
||||||
pdo_pgsql \
|
|
||||||
pgsql \
|
|
||||||
pdo_mysql \
|
|
||||||
gd \
|
|
||||||
zip \
|
|
||||||
opcache \
|
|
||||||
bcmath \
|
|
||||||
exif \
|
|
||||||
pcntl \
|
|
||||||
intl \
|
|
||||||
sockets \
|
|
||||||
&& pecl install redis \
|
|
||||||
&& docker-php-ext-enable redis \
|
|
||||||
&& apk del $PHPIZE_DEPS \
|
|
||||||
&& rm -rf /tmp/* /var/cache/apk/*
|
|
||||||
|
|
||||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
|
||||||
|
|
||||||
WORKDIR /var/www/html
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Stage 2: composer-deps — full PHP deps (incl. dev) for tooling/asset build
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
FROM system-base AS composer-deps
|
|
||||||
|
|
||||||
COPY composer.json composer.lock ./
|
|
||||||
RUN composer install \
|
|
||||||
--no-scripts \
|
|
||||||
--no-autoloader \
|
|
||||||
--prefer-dist \
|
|
||||||
--no-interaction
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Stage 3: composer-deps-prod — production deps only (no dev tooling)
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
FROM system-base AS composer-deps-prod
|
|
||||||
|
|
||||||
COPY composer.json composer.lock ./
|
|
||||||
RUN composer install \
|
|
||||||
--no-dev \
|
|
||||||
--no-scripts \
|
|
||||||
--no-autoloader \
|
|
||||||
--prefer-dist \
|
|
||||||
--no-interaction
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Stage 4: asset-build — wayfinder TS gen + Vite + Inertia SSR build
|
|
||||||
#
|
|
||||||
# PHP and Node together in one stage because @laravel/vite-plugin-wayfinder
|
|
||||||
# invokes `php artisan wayfinder:generate` during vite build — splitting
|
|
||||||
# the stages would mean the vite phase has no PHP available.
|
|
||||||
#
|
|
||||||
# Vite inlines VITE_* values into the JS bundle at build time. Pass them
|
|
||||||
# as build args (--build-arg VITE_REVERB_APP_KEY=...) so the bundle reaches
|
|
||||||
# the browser with the correct Reverb/PostHog config. Runtime env vars on
|
|
||||||
# the container have no effect on the already-compiled bundle.
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
FROM composer-deps AS asset-build
|
|
||||||
|
|
||||||
# Node 22 for Vite + npm scripts.
|
|
||||||
RUN apk add --no-cache nodejs npm
|
|
||||||
|
|
||||||
ARG VITE_APP_NAME=TryPost
|
|
||||||
ARG VITE_REVERB_APP_KEY=
|
|
||||||
ARG VITE_REVERB_HOST=localhost
|
|
||||||
ARG VITE_REVERB_PORT=8080
|
|
||||||
ARG VITE_REVERB_SCHEME=http
|
|
||||||
ARG VITE_POSTHOG_ENABLED=false
|
|
||||||
ARG VITE_POSTHOG_API_KEY=
|
|
||||||
ARG VITE_POSTHOG_HOST=https://us.i.posthog.com
|
|
||||||
|
|
||||||
# Stub PHP envs so artisan can boot (overridden at runtime).
|
|
||||||
ENV APP_KEY=base64:c3R1Yi13YXlmaW5kZXItZ2VuLWtleS1mb3ItYnVpbGRpbmctYXNzZXRzMA== \
|
|
||||||
APP_ENV=production \
|
|
||||||
APP_DEBUG=false \
|
|
||||||
APP_URL=http://localhost \
|
|
||||||
VITE_APP_NAME=${VITE_APP_NAME} \
|
|
||||||
VITE_REVERB_APP_KEY=${VITE_REVERB_APP_KEY} \
|
|
||||||
VITE_REVERB_HOST=${VITE_REVERB_HOST} \
|
|
||||||
VITE_REVERB_PORT=${VITE_REVERB_PORT} \
|
|
||||||
VITE_REVERB_SCHEME=${VITE_REVERB_SCHEME} \
|
|
||||||
VITE_POSTHOG_ENABLED=${VITE_POSTHOG_ENABLED} \
|
|
||||||
VITE_POSTHOG_API_KEY=${VITE_POSTHOG_API_KEY} \
|
|
||||||
VITE_POSTHOG_HOST=${VITE_POSTHOG_HOST}
|
|
||||||
|
|
||||||
COPY --from=composer-deps /var/www/html/vendor ./vendor
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Ensure Laravel runtime dirs exist — .dockerignore strips their contents
|
|
||||||
# (correct: caches shouldn't bloat builds), but artisan boot needs the dirs.
|
|
||||||
RUN mkdir -p storage/framework/cache/data \
|
|
||||||
storage/framework/sessions \
|
|
||||||
storage/framework/views \
|
|
||||||
storage/logs \
|
|
||||||
bootstrap/cache
|
|
||||||
|
|
||||||
RUN composer dump-autoload --no-scripts --optimize \
|
|
||||||
&& php artisan wayfinder:generate --with-form \
|
|
||||||
&& npm ci --no-audit --no-fund \
|
|
||||||
&& npm run build \
|
|
||||||
&& npm run build:ssr
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Stage 6: dev — local development image (bind-mount the source at runtime)
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
FROM system-base AS dev
|
|
||||||
|
|
||||||
ARG UID=1000
|
|
||||||
ARG GID=1000
|
|
||||||
|
|
||||||
# Node + npm for in-container Vite, npm scripts, ad-hoc tooling.
|
|
||||||
RUN apk add --no-cache nodejs npm
|
|
||||||
|
|
||||||
# Create non-root app user matching host UID/GID for clean bind-mount writes.
|
|
||||||
RUN groupmod -g "${GID}" www-data 2>/dev/null || groupadd -g "${GID}" app \
|
|
||||||
&& (id -u app >/dev/null 2>&1 || useradd -u "${UID}" -g "${GID}" -d /home/app -m -s /bin/bash app) \
|
|
||||||
&& chown -R "${UID}:${GID}" /var/www/html
|
|
||||||
|
|
||||||
COPY docker/nginx.conf /etc/nginx/http.d/default.conf
|
|
||||||
COPY docker/php.dev.ini /usr/local/etc/php/conf.d/99-trypost.ini
|
|
||||||
COPY docker/supervisord.dev.conf /etc/supervisor/conf.d/supervisord.conf
|
|
||||||
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
||||||
COPY docker/.env.docker.example /var/www/html/.env.docker.example
|
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
||||||
|
|
||||||
ENV TRYPOST_TARGET=dev
|
|
||||||
|
|
||||||
EXPOSE 80 5173 8080
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=5 \
|
|
||||||
CMD curl -fsS http://127.0.0.1/up || exit 1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Stage 7: production — self-contained image for self-hosters
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
FROM system-base AS production
|
|
||||||
|
|
||||||
# php-fpm runs as www-data (Alpine default pool config) — that's the user
|
|
||||||
# storage and bootstrap/cache must be owned by, so Laravel can write logs,
|
|
||||||
# session files, view cache, etc.
|
|
||||||
|
|
||||||
COPY docker/nginx.conf /etc/nginx/http.d/default.conf
|
|
||||||
COPY docker/php.prod.ini /usr/local/etc/php/conf.d/99-trypost.ini
|
|
||||||
COPY docker/supervisord.prod.conf /etc/supervisor/conf.d/supervisord.conf
|
|
||||||
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
||||||
RUN chmod +x /usr/local/bin/entrypoint.sh
|
|
||||||
|
|
||||||
# Application source + generated wayfinder TS + built assets — all from
|
|
||||||
# asset-build (the merged stage that ran php artisan wayfinder:generate,
|
|
||||||
# npm run build, and npm run build:ssr).
|
|
||||||
COPY --from=asset-build /var/www/html /var/www/html
|
|
||||||
|
|
||||||
# Replace the dev-equipped vendor/ with production-only deps. composer-deps-prod
|
|
||||||
# is built independently and is much smaller (no phpunit, no debugbar, no pail,
|
|
||||||
# no pint). Also wipe bootstrap/cache/*.php — package:discover ran with dev
|
|
||||||
# deps and recorded providers (Pail, Telescope etc) that no longer exist;
|
|
||||||
# Laravel rediscovers cleanly at first boot.
|
|
||||||
RUN rm -rf /var/www/html/vendor /var/www/html/bootstrap/cache/*.php
|
|
||||||
COPY --from=composer-deps-prod /var/www/html/vendor /var/www/html/vendor
|
|
||||||
|
|
||||||
RUN composer dump-autoload --optimize --classmap-authoritative --no-scripts \
|
|
||||||
&& chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
|
|
||||||
|
|
||||||
ENV TRYPOST_TARGET=production
|
|
||||||
|
|
||||||
EXPOSE 80 8080
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
||||||
CMD curl -fsS http://127.0.0.1/up || exit 1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# TryPost — container entrypoint. Idempotent first-run setup, then exec supervisord.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cd /var/www/html
|
|
||||||
|
|
||||||
TARGET="${TRYPOST_TARGET:-dev}"
|
|
||||||
|
|
||||||
# One-off commands from `docker compose run app ...` must bypass the long-lived
|
|
||||||
# application bootstrap and execute exactly as requested.
|
|
||||||
if [ "$#" -gt 0 ]; then
|
|
||||||
exec "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${TARGET}" = "production" ] && [ -z "${APP_KEY:-}" ]; then
|
|
||||||
echo "[entrypoint] APP_KEY is required in production" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 1) Bootstrap .env from the Docker template on first dev boot. The bind-mount
|
|
||||||
# in dev hides /var/www/html/.env.docker.example, so prefer docker/ first.
|
|
||||||
if [ "${TRYPOST_DOCKER_BOOTSTRAP:-0}" = "1" ] && [ ! -f .env ]; then
|
|
||||||
if [ -f docker/.env.docker.example ]; then
|
|
||||||
echo "[entrypoint] seeding .env from docker/.env.docker.example"
|
|
||||||
cp docker/.env.docker.example .env
|
|
||||||
elif [ -f .env.docker.example ]; then
|
|
||||||
echo "[entrypoint] seeding .env from .env.docker.example"
|
|
||||||
cp .env.docker.example .env
|
|
||||||
fi
|
|
||||||
# Hand the seeded .env over to the host user so they can edit it.
|
|
||||||
chown "${UID:-1000}:${GID:-1000}" .env 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2) Skip-bootstrap escape hatch for advanced users.
|
|
||||||
if [ "${TRYPOST_SKIP_BOOTSTRAP:-0}" = "1" ]; then
|
|
||||||
echo "[entrypoint] TRYPOST_SKIP_BOOTSTRAP=1 — exec'ing supervisord without setup"
|
|
||||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 3) Re-install composer deps if vendor was wiped (down -v in dev).
|
|
||||||
if [ "${TARGET}" = "dev" ] && [ ! -f vendor/autoload.php ]; then
|
|
||||||
echo "[entrypoint] vendor/ missing — running composer install"
|
|
||||||
composer install --no-interaction --prefer-dist
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4) Re-install node_modules if empty/wiped (dev only; vite needs them).
|
|
||||||
# Anonymous volumes pre-create the dir, so check for npm's lockfile instead.
|
|
||||||
if [ "${TARGET}" = "dev" ] && [ ! -f node_modules/.package-lock.json ]; then
|
|
||||||
echo "[entrypoint] node_modules/ empty — running npm ci"
|
|
||||||
npm ci --no-audit --no-fund
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 5) APP_KEY — generate on first boot if blank.
|
|
||||||
if [ -f .env ] && ! grep -qE '^APP_KEY=base64:' .env; then
|
|
||||||
echo "[entrypoint] generating APP_KEY"
|
|
||||||
php artisan key:generate --force
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 6) Wait for Postgres to be reachable.
|
|
||||||
DB_HOST_VALUE="${DB_HOST:-pgsql}"
|
|
||||||
DB_PORT_VALUE="${DB_PORT:-5432}"
|
|
||||||
DB_USER_VALUE="${DB_USERNAME:-postgres}"
|
|
||||||
DB_NAME_VALUE="${DB_DATABASE:-trypost}"
|
|
||||||
|
|
||||||
echo "[entrypoint] waiting for postgres at ${DB_HOST_VALUE}:${DB_PORT_VALUE}"
|
|
||||||
WAIT_ATTEMPTS=0
|
|
||||||
until pg_isready -h "${DB_HOST_VALUE}" -p "${DB_PORT_VALUE}" -U "${DB_USER_VALUE}" -d "${DB_NAME_VALUE}" >/dev/null 2>&1; do
|
|
||||||
WAIT_ATTEMPTS=$((WAIT_ATTEMPTS + 1))
|
|
||||||
if [ "${WAIT_ATTEMPTS}" -gt 60 ]; then
|
|
||||||
echo "[entrypoint] postgres not reachable after 60s — continuing anyway"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# 7) Run migrations (graceful: succeeds even when nothing to migrate).
|
|
||||||
echo "[entrypoint] running migrations"
|
|
||||||
php artisan migrate --force
|
|
||||||
|
|
||||||
# 8) storage:link if missing.
|
|
||||||
if [ ! -L public/storage ]; then
|
|
||||||
echo "[entrypoint] linking storage"
|
|
||||||
php artisan storage:link --force || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 9) Passport keys. Prefer PASSPORT_PRIVATE_KEY / PASSPORT_PUBLIC_KEY from
|
|
||||||
# the environment (required for durable / multi-node deploys — storage/oauth-*
|
|
||||||
# is not on a persisted volume in compose.prod.yaml). Fall back to generating
|
|
||||||
# files under storage/ only for local/dev when those env vars are unset.
|
|
||||||
if [ -n "${PASSPORT_PRIVATE_KEY:-}" ] && [ -n "${PASSPORT_PUBLIC_KEY:-}" ]; then
|
|
||||||
echo "[entrypoint] using Passport keys from environment"
|
|
||||||
elif [ "${TRYPOST_TARGET:-}" = "production" ] || [ "${APP_ENV:-}" = "production" ]; then
|
|
||||||
echo "[entrypoint] ERROR: PASSPORT_PRIVATE_KEY and PASSPORT_PUBLIC_KEY must be set in production." >&2
|
|
||||||
echo "[entrypoint] Generate once with: php artisan passport:keys --show" >&2
|
|
||||||
exit 1
|
|
||||||
elif [ ! -f storage/oauth-private.key ] || [ ! -f storage/oauth-public.key ]; then
|
|
||||||
echo "[entrypoint] generating Passport keys (dev fallback)"
|
|
||||||
php artisan passport:keys --force
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 10) Personal access client for REST API keys. The seeder is idempotent, so
|
|
||||||
# fresh self-hosted installs and existing deployments are both safe.
|
|
||||||
echo "[entrypoint] ensuring Passport personal access client"
|
|
||||||
php artisan db:seed --class='Database\Seeders\PassportSeeder' --force
|
|
||||||
|
|
||||||
# 11) Wayfinder TS regen — Vite needs the files before it boots.
|
|
||||||
echo "[entrypoint] regenerating wayfinder helpers"
|
|
||||||
php artisan wayfinder:generate --with-form || true
|
|
||||||
|
|
||||||
# 12) Cache strategy: prod = pre-cache; dev = clear.
|
|
||||||
if [ "${TARGET}" = "production" ]; then
|
|
||||||
php artisan config:cache
|
|
||||||
php artisan route:cache
|
|
||||||
php artisan view:cache
|
|
||||||
php artisan event:cache
|
|
||||||
else
|
|
||||||
php artisan config:clear
|
|
||||||
php artisan route:clear
|
|
||||||
php artisan view:clear
|
|
||||||
php artisan event:clear
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 13) Permissions. Production php-fpm pool runs as www-data (Alpine default),
|
|
||||||
# so storage and bootstrap/cache must be writable by that user — Laravel
|
|
||||||
# needs to write session files, view cache, log files, etc.
|
|
||||||
if [ "${TARGET}" = "production" ]; then
|
|
||||||
chown -R www-data:www-data storage bootstrap/cache
|
|
||||||
else
|
|
||||||
# Dev: ensure UID-mapped user owns runtime dirs.
|
|
||||||
APP_UID="${UID:-1000}"
|
|
||||||
APP_GID="${GID:-1000}"
|
|
||||||
chown -R "${APP_UID}:${APP_GID}" storage bootstrap/cache 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[entrypoint] ready — handing off to supervisord"
|
|
||||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
server_name _;
|
|
||||||
root /var/www/html/public;
|
|
||||||
index index.php;
|
|
||||||
|
|
||||||
charset utf-8;
|
|
||||||
client_max_body_size 1G;
|
|
||||||
|
|
||||||
# Reverb WebSocket (Pusher protocol). The browser connects to
|
|
||||||
# wss://<host>/app/<key>; the broadcaster REST API lives under /apps/.
|
|
||||||
# Both are proxied to the in-container Reverb on 127.0.0.1:8080.
|
|
||||||
location /app/ {
|
|
||||||
proxy_pass http://127.0.0.1:8080;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_read_timeout 86400s;
|
|
||||||
proxy_send_timeout 86400s;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /apps/ {
|
|
||||||
proxy_pass http://127.0.0.1:8080;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.php?$query_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /favicon.ico { access_log off; log_not_found off; }
|
|
||||||
location = /robots.txt { access_log off; log_not_found off; }
|
|
||||||
|
|
||||||
error_page 404 /index.php;
|
|
||||||
|
|
||||||
location ~ \.php$ {
|
|
||||||
fastcgi_pass 127.0.0.1:9000;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
|
||||||
fastcgi_read_timeout 120s;
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_hide_header X-Powered-By;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ /\.(?!well-known).* {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
; TryPost — PHP settings for local development.
|
|
||||||
memory_limit = 512M
|
|
||||||
upload_max_filesize = 1G
|
|
||||||
post_max_size = 1G
|
|
||||||
max_execution_time = 120
|
|
||||||
max_input_vars = 5000
|
|
||||||
|
|
||||||
; Hot-reload PHP edits without restarting FPM.
|
|
||||||
opcache.enable = 1
|
|
||||||
opcache.memory_consumption = 128
|
|
||||||
opcache.max_accelerated_files = 10000
|
|
||||||
opcache.validate_timestamps = 1
|
|
||||||
opcache.revalidate_freq = 0
|
|
||||||
|
|
||||||
; Surface errors loudly in dev.
|
|
||||||
display_errors = On
|
|
||||||
display_startup_errors = On
|
|
||||||
error_reporting = E_ALL
|
|
||||||
log_errors = On
|
|
||||||
error_log = /proc/self/fd/2
|
|
||||||
|
|
||||||
; Xdebug optional — enable via XDEBUG_MODE in compose.override.yaml.
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
; TryPost — PHP settings for production.
|
|
||||||
memory_limit = 256M
|
|
||||||
upload_max_filesize = 1G
|
|
||||||
post_max_size = 1G
|
|
||||||
max_execution_time = 60
|
|
||||||
max_input_vars = 5000
|
|
||||||
|
|
||||||
; OpCache hardened for production: no timestamp checks, larger interned-string buffer.
|
|
||||||
opcache.enable = 1
|
|
||||||
opcache.memory_consumption = 192
|
|
||||||
opcache.max_accelerated_files = 20000
|
|
||||||
opcache.validate_timestamps = 0
|
|
||||||
opcache.interned_strings_buffer = 16
|
|
||||||
|
|
||||||
; Security
|
|
||||||
expose_php = Off
|
|
||||||
display_errors = Off
|
|
||||||
display_startup_errors = Off
|
|
||||||
log_errors = On
|
|
||||||
error_log = /proc/self/fd/2
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Creates the test database expected by phpunit.xml on first Postgres boot.
|
|
||||||
# Postgres-alpine runs every *.sh / *.sql in /docker-entrypoint-initdb.d/ once,
|
|
||||||
# right after the primary database (POSTGRES_DB) is initialized.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" <<-EOSQL
|
|
||||||
CREATE DATABASE trypost_test;
|
|
||||||
GRANT ALL PRIVILEGES ON DATABASE trypost_test TO "${POSTGRES_USER}";
|
|
||||||
EOSQL
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
[supervisord]
|
|
||||||
nodaemon=true
|
|
||||||
user=root
|
|
||||||
logfile=/dev/null
|
|
||||||
logfile_maxbytes=0
|
|
||||||
pidfile=/run/supervisord.pid
|
|
||||||
|
|
||||||
[program:php-fpm]
|
|
||||||
command=php-fpm -F
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=10
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:nginx]
|
|
||||||
command=nginx -g 'daemon off;'
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=20
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:reverb]
|
|
||||||
command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=30
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:horizon]
|
|
||||||
command=php /var/www/html/artisan horizon
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=30
|
|
||||||
stopwaitsecs=10
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:scheduler]
|
|
||||||
command=php /var/www/html/artisan schedule:work
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=40
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:vite]
|
|
||||||
command=/bin/sh -c "exec /usr/bin/npm run dev -- --host 0.0.0.0"
|
|
||||||
directory=/var/www/html
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
startsecs=10
|
|
||||||
priority=50
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
[supervisord]
|
|
||||||
nodaemon=true
|
|
||||||
user=root
|
|
||||||
logfile=/dev/null
|
|
||||||
logfile_maxbytes=0
|
|
||||||
pidfile=/run/supervisord.pid
|
|
||||||
|
|
||||||
[program:php-fpm]
|
|
||||||
command=php-fpm -F
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=10
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:nginx]
|
|
||||||
command=nginx -g 'daemon off;'
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=20
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:reverb]
|
|
||||||
command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080
|
|
||||||
autostart=true
|
|
||||||
autorestart=unexpected
|
|
||||||
exitcodes=0,2
|
|
||||||
priority=30
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:horizon]
|
|
||||||
command=php /var/www/html/artisan horizon
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=30
|
|
||||||
stopwaitsecs=10
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
|
|
||||||
[program:scheduler]
|
|
||||||
command=php /var/www/html/artisan schedule:work
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
priority=40
|
|
||||||
stdout_logfile=/dev/stdout
|
|
||||||
stdout_logfile_maxbytes=0
|
|
||||||
stderr_logfile=/dev/stderr
|
|
||||||
stderr_logfile_maxbytes=0
|
|
||||||
Loading…
Reference in a new issue