Derived from events and kept per person: whoever a ticket is assigned to, whoever reports or works on it when it moves, fails QA or gets a comment, and whoever joins a project. Never about what you did yourself, once per event. They arrive live through a subscription to a bell in the header, and are copied by e-mail to whoever turns it on when SMTP_URL is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
145 lines
5.0 KiB
YAML
145 lines
5.0 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: ''
|
|
ATTACHMENTS_DIR: /data/attachments
|
|
ATTACHMENTS_MAX_MB: ${ATTACHMENTS_MAX_MB:-25}
|
|
# E-mail copies of notifications (optional).
|
|
SMTP_URL: ${SMTP_URL:-}
|
|
MAIL_FROM: ${MAIL_FROM:-}
|
|
WEB_URL: ${WEB_URL:-}
|
|
volumes:
|
|
# Files attached to tickets: lose this volume and the tickets point at nothing.
|
|
- attachments-data:/data/attachments
|
|
depends_on:
|
|
postgres: { condition: service_healthy }
|
|
mongo: { condition: service_healthy }
|
|
redis: { condition: service_healthy }
|
|
restart: unless-stopped
|
|
|
|
# MCP server for AI clients (Claude, Cursor…): Streamable HTTP at /mcp. It consumes the API
|
|
# with each caller's own Pireon token (Authorization header), so it holds no credentials.
|
|
# Optional: it only starts with the `mcp` profile (`docker compose --profile mcp up -d`; in
|
|
# Dokploy, COMPOSE_PROFILES=mcp) and needs a domain → service `mcp`, port 3010.
|
|
mcp:
|
|
profiles: [mcp]
|
|
build:
|
|
context: .
|
|
target: mcp
|
|
secrets: [nexus_token]
|
|
image: task-stream-mcp
|
|
expose: ['3010']
|
|
networks:
|
|
default:
|
|
aliases: [task-stream-mcp]
|
|
environment:
|
|
TASKSTREAM_API_URL: http://task-stream-api:3000
|
|
# Domains it answers on (protection against DNS rebinding): the one Dokploy gives it.
|
|
MCP_ALLOWED_HOSTS: ${MCP_ALLOWED_HOSTS:-mcp.taskstream.pireon.dev,localhost}
|
|
MCP_LOG_LEVEL: ${MCP_LOG_LEVEL:-warn}
|
|
depends_on:
|
|
api: { 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:
|
|
attachments-data:
|
|
postgres-data:
|
|
mongo-data:
|
|
redis-data:
|
|
|
|
secrets:
|
|
nexus_token:
|
|
environment: NEXUS_TOKEN
|