Files
lasuite-docs/compose.yml
T
Kevin JahnsandAnthony LC 3a4fb9a8aa ⬆️(collaboration) upgrade yhub to 0.9.0 and really delete superseded blobs
The S3 persistence plugin records the version id of the object it wrote
and names that version when it deletes it. On a versioned bucket - what
a deployment runs - a delete that names no version deletes nothing: it
writes a delete marker and keeps every version underneath. Each
compaction supersedes the blobs of the one before, so what was kept was
every version of every document ever written, a document someone asked
to erase included, still readable by anyone who can list versions. On
AWS this needs s3:DeleteObjectVersion, which a policy granting
s3:DeleteObject alone does not cover.

Blobs are written to the bucket for every branch of a document.

YHUB_S3_PERSISTENCE now governs only whether new blobs are written
there. The plugin itself is attached whenever the YHUB_S3_* settings
name a bucket, on or off, because reading is the half that must never
be taken away: a row pointing at an object is unreadable without the
plugin that wrote it, and yhub reports such a version as having no
content rather than as an error. Turning the toggle off stops the
writing and leaves the reading alone; it is the settings, not the
toggle, that a deployment whose bucket holds anything must keep. Half a
configuration is a startup error naming what is missing, as before.

The dev stack keeps the toggle off and creates its bucket versioned, so
flipping it on exercises what a deployment runs rather than a simpler
case. Its createbuckets job needed fixing to do so: the folded yaml
block joins its lines with a space, so the trailing backslashes reached
the shell as an escaped space glued to the next word and everything
past the first && silently did nothing - the media bucket never had
versioning enabled either.

Signed-off-by: Kevin Jahns <kevin.jahns@protonmail.com>
2026-09-01 15:33:26 +02:00

343 lines
9.5 KiB
YAML

name: docs
services:
postgresql:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 1s
timeout: 2s
retries: 300
env_file:
- env.d/development/postgresql
- env.d/development/postgresql.local
ports:
- "15432:5432"
volumes:
- ./docker/files/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:ro
redis:
image: redis:5
mailcatcher:
image: sj26/mailcatcher:latest
ports:
- "1081:1080"
minio:
user: ${DOCKER_USER:-1000}
image: minio/minio
environment:
- MINIO_ROOT_USER=impress
- MINIO_ROOT_PASSWORD=password
ports:
- "9000:9000"
- "9001:9001"
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 1s
timeout: 20s
retries: 300
entrypoint: ""
command: minio server --console-address :9001 /data
volumes:
- ./data/media:/data
createbuckets:
image: minio/mc
depends_on:
minio:
condition: service_healthy
restart: true
# The yhub bucket is made here rather than left to the collaboration
# server, which creates the bucket it does not find with no versioning on
# it. Versioning is what a deployment runs, and it is the case where
# deleting an object needs the version named to delete anything at all
# (YHUB_S3_PERSISTENCE, src/yhub-server/README.md).
#
# The lines below end on `&&` and not on a backslash: yaml folds this block
# by joining its lines with a space, so a trailing `\` reaches the shell as
# an escaped space glued to the word after it — ` /usr/bin/mc`, which is no
# command, and everything past the first `&&` silently did nothing.
entrypoint: >
sh -c "
/usr/bin/mc alias set impress http://minio:9000 impress password &&
/usr/bin/mc mb --ignore-existing impress/impress-media-storage &&
/usr/bin/mc version enable impress/impress-media-storage &&
/usr/bin/mc mb --ignore-existing impress/yhub-storage &&
/usr/bin/mc version enable impress/yhub-storage &&
exit 0;"
app-dev:
build:
context: .
target: backend-development
args:
DOCKER_USER: ${DOCKER_USER:-1000}
user: ${DOCKER_USER:-1000}
image: impress:backend-development
environment:
- PYLINTHOME=/app/.pylint.d
- DJANGO_CONFIGURATION=Development
env_file:
- env.d/development/common
- env.d/development/common.local
- env.d/development/postgresql
- env.d/development/postgresql.local
ports:
- "8071:8000"
networks:
default: {}
lasuite:
aliases:
- impress
volumes:
- ./src/backend:/app
- ./data/static:/data/static
- ./data/jwt:/data/jwt:ro
- /app/.venv
depends_on:
postgresql:
condition: service_healthy
restart: true
mailcatcher:
condition: service_started
redis:
condition: service_started
createbuckets:
condition: service_started
celery-dev:
user: ${DOCKER_USER:-1000}
image: impress:backend-development
command: ["celery", "-A", "impress.celery_app", "worker", "-l", "DEBUG"]
environment:
- DJANGO_CONFIGURATION=Development
networks:
- default
- lasuite
env_file:
- env.d/development/common
- env.d/development/common.local
- env.d/development/postgresql
- env.d/development/postgresql.local
volumes:
- ./src/backend:/app
- ./data/static:/data/static
- ./data/jwt:/data/jwt:ro
- /app/.venv
depends_on:
- app-dev
nginx:
image: nginx:1.25
ports:
- "8083:8083"
networks:
default: {}
lasuite:
aliases:
- nginx
volumes:
- ./docker/files/etc/nginx/conf.d:/etc/nginx/conf.d:ro
depends_on:
app-dev:
condition: service_started
keycloak:
condition: service_healthy
restart: true
nginx-frontend:
image: nginx:1.25
ports:
- "3000:3000"
volumes:
- ./src/frontend/apps/impress/conf/default.conf:/etc/nginx/conf.d/impress.conf
- ./src/frontend/apps/impress/out:/app
depends_on:
keycloak:
condition: service_healthy
restart: true
frontend-development:
user: "${DOCKER_USER:-1000}"
build:
context: .
dockerfile: ./src/frontend/Dockerfile
target: impress-dev
args:
API_ORIGIN: "http://localhost:8071"
PUBLISH_AS_MIT: "false"
SW_DEACTIVATED: "true"
image: impress:frontend-development
volumes:
- ./src/frontend:/home/frontend
- /home/frontend/node_modules
- /home/frontend/apps/impress/node_modules
ports:
- "3000:3000"
crowdin:
image: crowdin/cli:3.16.0
volumes:
- ".:/app"
env_file:
- env.d/development/crowdin
- env.d/development/crowdin.local
user: "${DOCKER_USER:-1000}"
working_dir: /app
node:
image: node:22
user: "${DOCKER_USER:-1000}"
environment:
HOME: /tmp
volumes:
- ".:/app"
y-provider-development-converter:
user: ${DOCKER_USER:-1000}
build:
context: .
dockerfile: ./src/frontend/servers/y-provider/Dockerfile
target: y-provider-development
image: impress:y-provider-development
restart: unless-stopped
env_file:
- env.d/development/common
- env.d/development/common.local
volumes:
- ./src/frontend/:/home/frontend
- /home/frontend/node_modules
- /home/frontend/servers/y-provider/node_modules
yhub-valkey:
image: valkey/valkey:alpine
# volatile-lru per yhub DEPLOYMENT.md; AOF because valkey is the authoritative store
# for updates the worker hasn't persisted yet (up to taskDebounce+minMessageLifetime)
command: ["valkey-server", "--maxmemory-policy", "volatile-lru",
"--appendonly", "yes", "--appendfsync", "everysec"]
volumes:
- yhub-valkey-data:/data
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 1s
timeout: 2s
retries: 60
yhub-postgres:
image: postgres:16-alpine
env_file:
- env.d/development/yhub-postgres
volumes:
- yhub-pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U yhub"]
interval: 1s
timeout: 2s
retries: 60
# no published port (Django's postgres already publishes)
# the schema is not seeded here: initdb.d would only replay on a fresh
# volume, so an upgrade that adds a table would silently skip an existing
# one. `make migrate-yhub` runs yhub's own DDL script instead, the same way
# `make migrate` runs Django's migrations.
yhub:
user: ${DOCKER_USER:-1000}
build:
context: .
dockerfile: ./src/yhub-server/Dockerfile
target: yhub-development
image: impress:yhub-development
environment:
HOME: /tmp # same reason as node-based services above (unmapped uid)
# its own file rather than the backend's: this server reads none of the
# django settings `common` carries, and everything it does read is in there
env_file:
- env.d/development/yhub
- env.d/development/yhub.local
volumes:
- ./data/jwt:/data/jwt:ro
# editing a source file restarts the server (nodemon), no rebuild
- ./src/yhub-server:/app
# node_modules is installed in the image, not in the source tree: keep
# the bind mount above from hiding it
- /app/node_modules
restart: unless-stopped
ports:
- "3002:3002"
depends_on:
yhub-valkey:
condition: service_healthy
yhub-postgres:
condition: service_healthy
# soft migration reads the legacy document store at startup traffic —
# starting before minio would cache 401s for the first accessed docs —
# and YHUB_S3_PERSISTENCE checks its own bucket at boot. Waiting for the
# buckets to be *made*, not merely for the job to have started: the
# collaboration server creates the bucket it does not find, and the one
# it creates has no versioning
createbuckets:
condition: service_completed_successfully
minio:
condition: service_healthy
kc_postgresql:
image: postgres:14.3
healthcheck:
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
interval: 1s
timeout: 2s
retries: 300
ports:
- "5433:5432"
env_file:
- env.d/development/kc_postgresql
- env.d/development/kc_postgresql.local
keycloak:
image: quay.io/keycloak/keycloak:26.3
volumes:
- ./docker/auth/realm.json:/opt/keycloak/data/import/realm.json
env_file:
- env.d/development/kc_auth
- env.d/development/kc_auth.local
command:
- start-dev
- --features=preview
- --import-realm
- --hostname-strict=false
- --health-enabled=true
- --metrics-enabled=true
healthcheck:
test:
[
"CMD-SHELL",
'exec 3<>/dev/tcp/localhost/9000; echo -e "GET /health/live HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" >&3; grep "HTTP/1.1 200 OK" <&3',
]
start_period: 5s
interval: 1s
timeout: 2s
retries: 300
ports:
- "8080:8080"
depends_on:
kc_postgresql:
condition: service_healthy
restart: true
docspec:
image: ghcr.io/docspecio/api:3.0.1
ports:
- "4000:4000"
networks:
lasuite:
name: lasuite-network
driver: bridge
external: true
volumes:
yhub-pgdata: {}
yhub-valkey-data: {}