Initial commit: Pireon Admin — headless admin panel and public API

Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 17:55:27 +02:00
commit 3897113313
534 changed files with 64050 additions and 0 deletions

118
.env.production.example Normal file
View File

@@ -0,0 +1,118 @@
###############################################################################
# Production environment for the Docker stack.
#
# cp .env.production.example .env.production
# docker compose run --rm --no-deps app php artisan key:generate --show
#
# Everything the containers need is here; docker-compose.yml sets the database,
# cache, session and queue drivers itself so they cannot drift.
###############################################################################
APP_NAME="Pireon Admin"
APP_ENV=production
APP_DEBUG=false
# Required. Without it sessions cannot be decrypted and the signed preview
# links cannot be verified — the entrypoint refuses to start rather than
# failing later in a way nobody connects to this.
APP_KEY=
# The public address of the panel itself, https included. It is what the API
# builds absolute image URLs from, so getting it wrong means every image on the
# public site points at the wrong host.
APP_URL=https://admin.example.org
APP_LOCALE=es
APP_FALLBACK_LOCALE=es
LOG_CHANNEL=stack
LOG_STACK=single
LOG_LEVEL=warning
# Port the stack listens on. Put a reverse proxy in front of it and terminate
# TLS there.
APP_PORT=8000
###############################################################################
# Database
###############################################################################
DB_DATABASE=pireon
DB_USERNAME=pireon
# No default on purpose: compose refuses to start without it.
DB_PASSWORD=
###############################################################################
# Pireon OAuth2 identity server
#
# The panel's sign-in form uses the password grant, so the registered client
# must allow it.
###############################################################################
PIREON_SERVER=https://oauth.pireon.dev
PIREON_CLIENT_ID=
PIREON_CLIENT_SECRET=
PIREON_REDIRECT_URI="${APP_URL}/auth/callback"
PIREON_SCOPES=account:read
PIREON_PASSWORD_GRANT=true
PIREON_INTROSPECTION_TTL=60
PIREON_DEFAULT_ROLE=viewer
PIREON_REQUIRE_ACTIVE=true
PIREON_REQUIRE_APPROVED=true
###############################################################################
# Public site (separate, server-rendered application)
###############################################################################
SITE_URL=https://example.org
SITE_PREFIX_DEFAULT_LOCALE=false
SITE_CACHE_SECONDS=60
# Generate with: php -r "echo bin2hex(random_bytes(24)), PHP_EOL;"
SITE_PREVIEW_TOKEN=
# With SSR every visitor's page view arrives from the frontend's address, so
# this budget is for the whole site at once. Add that address here and it
# bypasses the limit entirely.
SITE_RATE_LIMIT=600
SITE_TRUSTED_IPS=
# Shared secret for the 404 reports the frontend sends back. Empty disables
# the endpoint. Generate with: php -r "echo bin2hex(random_bytes(24)), PHP_EOL;"
SITE_INGEST_TOKEN=
SITE_TELEMETRY_RATE_LIMIT=60
SITE_TELEMETRY_MAX_BATCH=50
SITE_ACCEPT_SUBMISSIONS=true
SITE_HONEYPOT_FIELD=website
SITE_SUBMISSION_RATE_LIMIT=3
SITE_SUBMISSION_HOURLY_LIMIT=60
SITE_SUBMISSION_DEDUPE_MINUTES=1440
SITE_SUBMISSION_MAIL=false
###############################################################################
# Images
#
# `public` is right for this stack: the storage volume is mounted in the web
# container and Caddy serves /storage/* straight off disk without touching PHP.
###############################################################################
MEDIA_DISK=public
MEDIA_MAX_PIXELS=50000000
###############################################################################
# Git repositories
###############################################################################
GIT_PROVIDER=gitea
GITEA_URL=
GITEA_TOKEN=
GITEA_ORG=
GIT_SYNC_CRON=hourly
###############################################################################
# Mail — only needed if SITE_SUBMISSION_MAIL is true
###############################################################################
MAIL_MAILER=log
MAIL_HOST=
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_FROM_ADDRESS="admin@example.org"
MAIL_FROM_NAME="${APP_NAME}"