Files
task-stream/docker-compose.yml
Josep Piguillem cbaad17fc7 fix(auth): separate Pireon clients for authorization code and password
Pireon OAuth clients allow a single grant type. With one PIREON_CLIENT_ID of
type password, email + password worked but "Sign in with Pireon" failed with
`unauthorized_client`.

- PIREON_CLIENT_ID/SECRET: authorization_code client (Sign in with Pireon,
  Swagger UI). New PIREON_PASSWORD_CLIENT_ID/SECRET: password client, falling
  back to the former so existing setups keep working.
- Refresh tries both clients: a refresh token only works with its issuer.
- PIREON_PASSWORD_GRANT=false now also rejects password grants in the API
  (before it only hid the form).
- /api/auth/config returns clientId: null when there is no authorization_code
  client, and the web hides "Sign in with Pireon".
- docker-compose passes the new variables; docs 05/07, .env.example,
  openapi.json; unit tests for the client selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:41:50 +02:00

111 lines
3.7 KiB
YAML

# TaskStream: web + API + infrastructure — production definition (Dokploy deploys this file).
# No ports are published here: services only talk over the compose network, and the web is
# exposed through the platform's proxy (Dokploy domain → service `web`, port 80).
#
# Services talk to each other by unique aliases (task-stream-*), never by the bare service
# name: on a shared network (Dokploy's dokploy-network) other projects also have an `api`,
# `postgres` or `redis`, and Docker DNS would hand out theirs too.
#
# Locally, `docker compose` also loads docker-compose.override.yml, which publishes the ports:
# docker compose up -d --build → everything: web on http://localhost:8080
# docker compose up -d postgres mongo redis → only infrastructure (then `npx nx serve api` / `npx nx serve web`)
#
# Building needs access to Pireon's Nexus (@pi/ui): set NEXUS_TOKEN in the environment or in .env
# (Dokploy: Environment tab). It reaches the build as a secret and never lands in an image layer.
services:
web:
build:
context: .
target: web
secrets: [nexus_token]
image: task-stream-web
expose: ['80']
networks: [default]
depends_on:
api: { condition: service_healthy }
restart: unless-stopped
api:
build:
context: .
target: api
secrets: [nexus_token]
image: task-stream-api
expose: ['3000']
networks:
default:
aliases: [task-stream-api]
environment:
POSTGRES_URL: postgres://taskstream:taskstream@task-stream-postgres:5432/taskstream
# No migrations yet: TypeORM creates the event store and lookup tables.
POSTGRES_SYNCHRONIZE: 'true'
MONGO_URL: mongodb://task-stream-mongo:27017/taskstream_read
REDIS_URL: redis://task-stream-redis:6379
EVENT_STREAM_KEY: task-stream:events
EVENTS_BRIDGE_ENABLED: 'true'
PIREON_BASE_URL: https://oauth.pireon.dev
PIREON_WEBHOOK_SECRET: ${PIREON_WEBHOOK_SECRET:-}
PIREON_CLIENT_ID: ${PIREON_CLIENT_ID:-}
PIREON_CLIENT_SECRET: ${PIREON_CLIENT_SECRET:-}
# Pireon clients allow one grant type: the one above is authorization_code,
# this one password (email + password sign-in). Optional.
PIREON_PASSWORD_CLIENT_ID: ${PIREON_PASSWORD_CLIENT_ID:-}
PIREON_PASSWORD_CLIENT_SECRET: ${PIREON_PASSWORD_CLIENT_SECRET:-}
GRAPHQL_PLAYGROUND: 'true'
# The SDL / OpenAPI files are only useful in development (codegen); the
# document is still served at /api/docs-json.
GRAPHQL_SCHEMA_FILE: ''
OPENAPI_FILE: ''
depends_on:
postgres: { condition: service_healthy }
mongo: { condition: service_healthy }
redis: { condition: service_healthy }
restart: unless-stopped
postgres:
image: postgres:17-alpine
networks:
default:
aliases: [task-stream-postgres]
environment:
POSTGRES_USER: taskstream
POSTGRES_PASSWORD: taskstream
POSTGRES_DB: taskstream
volumes: [postgres-data:/var/lib/postgresql/data]
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U taskstream']
interval: 5s
retries: 10
mongo:
image: mongo:8
networks:
default:
aliases: [task-stream-mongo]
volumes: [mongo-data:/data/db]
healthcheck:
test: ['CMD', 'mongosh', '--quiet', '--eval', 'db.adminCommand("ping")']
interval: 5s
retries: 10
redis:
image: redis:7-alpine
networks:
default:
aliases: [task-stream-redis]
command: ['redis-server', '--appendonly', 'yes']
volumes: [redis-data:/data]
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 5s
retries: 10
volumes:
postgres-data:
mongo-data:
redis-data:
secrets:
nexus_token:
environment: NEXUS_TOKEN