Files
classroomio/docker/docker-compose.yaml
T
Rotimi Best d5b07d356e feat: HLS streaming, transcripts, agent course generation & landing-page course previews (#663)
New features:
- HLS video encoding/streaming (hls routes, hls-encoder) and a media
  transcripts pipeline (transcripts route + media-transcript queries)
- AI assistant chat vs. run model: agent-course-generation worker/queue,
  run-monitor pane, chat-changes card, content ask-ai bar
- Per-theme course-page previews across org landing-page themes (bold,
  classic, corporate, editorial, etc.) with a shared course token/component set
- View-as-student mode and login-link / link-invite invite flows

Refactor:
- Extract packages/core consolidating config (env, storage, dashboard-url),
  services and utils previously duplicated under apps/api/src/config
- Remove legacy agent service modules (chat-tools, chat-context,
  context-window, document, fetch-url, lesson-content)
- Relax Cross-Origin-Resource-Policy to allow cross-origin media embedding
- Add format script to tenant-router so turbo run format covers it
2026-06-07 18:43:05 +01:00

158 lines
5.6 KiB
YAML

services:
postgres:
image: postgres:16-alpine
container_name: cio-postgres
environment:
POSTGRES_DB: ${POSTGRES_DB:-classroomio}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
# Require a successful query so we only pass once Postgres is out of recovery
# (pg_isready can pass while DB is still in recovery, causing 57P03 on sign-in)
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-classroomio} && psql -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-classroomio} -tAc 'SELECT 1'",
]
interval: 5s
timeout: 5s
retries: 15
start_period: 10s
redis:
image: redis:7-alpine
container_name: cio-redis
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
start_period: 5s
minio:
image: minio/minio:latest
container_name: cio-minio
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
volumes:
- minio-data:/data
profiles:
- minio
minio-init:
image: minio/mc:latest
container_name: cio-minio-init
depends_on:
- minio
entrypoint: >
/bin/sh -c "
echo 'Waiting for MinIO to be ready...';
sleep 5;
mc alias set local http://minio:9000 $${MINIO_ROOT_USER:-minioadmin} $${MINIO_ROOT_PASSWORD:-minioadmin};
mc mb local/videos --ignore-existing;
mc mb local/documents --ignore-existing;
mc mb local/media --ignore-existing;
mc anonymous set download local/media;
echo 'MinIO buckets created';
exit 0;
"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
profiles:
- minio
api:
build:
context: ..
dockerfile: docker/Dockerfile.api
container_name: cio-api
ports:
- "3081:3081"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3081
PUBLIC_SERVER_URL: ${PUBLIC_SERVER_URL:-http://localhost:3081}
TRUSTED_ORIGINS: ${TRUSTED_ORIGINS:-}
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-classroomio}
PRIVATE_DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@postgres:5432/${POSTGRES_DB:-classroomio}
REDIS_URL: redis://redis:6379
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-docker-local-client-id}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-docker-local-client-secret}
SMTP_HOST: ${SMTP_HOST:-}
SMTP_USER: ${SMTP_USER:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
SMTP_PORT: ${SMTP_PORT:-}
SMTP_SENDER: ${SMTP_SENDER:-}
CLOUDFLARE_BUCKET_DOMAIN: ${CLOUDFLARE_BUCKET_DOMAIN:-}
CLOUDFLARE_ACCESS_KEY: ${CLOUDFLARE_ACCESS_KEY:-}
CLOUDFLARE_SECRET_ACCESS_KEY: ${CLOUDFLARE_SECRET_ACCESS_KEY:-}
CLOUDFLARE_ACCOUNT_ID: ${CLOUDFLARE_ACCOUNT_ID:-}
CLOUDFLARE_IMAGE_BUCKET_DOMAIN: ${CLOUDFLARE_IMAGE_BUCKET_DOMAIN:-}
CLOUDFLARE_RENDERING_API_KEY: ${CLOUDFLARE_RENDERING_API_KEY:-}
OBJECT_STORAGE_ENDPOINT: ${OBJECT_STORAGE_ENDPOINT:-}
OBJECT_STORAGE_PUBLIC_ENDPOINT: ${OBJECT_STORAGE_PUBLIC_ENDPOINT:-}
OBJECT_STORAGE_ACCESS_KEY_ID: ${OBJECT_STORAGE_ACCESS_KEY_ID:-}
OBJECT_STORAGE_SECRET_ACCESS_KEY: ${OBJECT_STORAGE_SECRET_ACCESS_KEY:-}
OBJECT_STORAGE_FORCE_PATH_STYLE: ${OBJECT_STORAGE_FORCE_PATH_STYLE:-true}
OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL: ${OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL:-}
AUTH_BEARER_TOKEN: ${AUTH_BEARER_TOKEN:-local-dev-api-key}
PRIVATE_SERVER_KEY: ${PRIVATE_SERVER_KEY:-local-dev-api-key}
DASHBOARD_ORIGIN: ${DASHBOARD_ORIGIN:-}
LICENSE_KEY: ${LICENSE_KEY:-}
UNSPLASH_API_KEY: ${UNSPLASH_API_KEY:-}
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-local-dev-only-secret-change-this}
SKIP_DB_SETUP: ${SKIP_DB_SETUP:-}
restart: unless-stopped
dashboard:
build:
context: ..
dockerfile: docker/Dockerfile.dashboard
args:
PUBLIC_IS_SELFHOSTED: ${PUBLIC_IS_SELFHOSTED:-true}
container_name: cio-dashboard
ports:
- "3082:3082"
depends_on:
api:
condition: service_started
environment:
NODE_ENV: production
PORT: 3082
ORIGIN: ${DASHBOARD_ORIGIN:-http://localhost:3082}
PUBLIC_IS_SELFHOSTED: ${PUBLIC_IS_SELFHOSTED:-true}
PRIVATE_SERVER_URL: ${PRIVATE_SERVER_URL:-http://api:3081}
PRIVATE_SERVER_KEY: ${PRIVATE_SERVER_KEY:-local-dev-api-key}
PRIVATE_APP_HOST: ${PRIVATE_APP_HOST:-localhost}
PRIVATE_APP_SUBDOMAINS: ${PRIVATE_APP_SUBDOMAINS:-app}
AUTH_BEARER_TOKEN: ${AUTH_BEARER_TOKEN:-}
ALLOWED_EXTERNAL_DOMAINS: ${ALLOWED_EXTERNAL_DOMAINS:-}
CSP_SCRIPT_SRC_DOMAINS: ${CSP_SCRIPT_SRC_DOMAINS:-}
CSP_STYLE_SRC_DOMAINS: ${CSP_STYLE_SRC_DOMAINS:-}
CSP_CONNECT_SRC_DOMAINS: ${CSP_CONNECT_SRC_DOMAINS:-}
CSP_FRAME_SRC_DOMAINS: ${CSP_FRAME_SRC_DOMAINS:-}
CSP_FONT_SRC_DOMAINS: ${CSP_FONT_SRC_DOMAINS:-}
CSP_MEDIA_SRC_DOMAINS: ${CSP_MEDIA_SRC_DOMAINS:-}
restart: unless-stopped
volumes:
postgres-data:
redis-data:
minio-data: