Adapt the refactored web player to the new backend API (id path params, bulk track adds, flat list items, mix kind) and port AddAlbumToPlayList to the backend conventions.
416 lines
19 KiB
YAML
416 lines
19 KiB
YAML
# Musify: base stack, shared by development and production.
|
|
#
|
|
# Postgres, Zitadel, SeaweedFS, RabbitMQ and Jaeger are NOT part of this
|
|
# file. They're shared infrastructure that lives in ../../../Infrastructure
|
|
# and must already be running (see Infrastructure/README.md). This file only
|
|
# has what's specific to Musify: its own database, its own OIDC apps and
|
|
# storage bucket (created against the shared instances below), and its own
|
|
# apps.
|
|
#
|
|
# dev docker compose -f deploy/compose.yml -f deploy/compose.dev.yml up -d
|
|
# prod docker compose --env-file deploy/.env.prod -f deploy/compose.yml -f deploy/compose.prod.yml up -d
|
|
#
|
|
# The four application services sit behind the `apps` profile, so a plain
|
|
# `up` runs only the one-shot bootstrap jobs (stream-ticket keys, database
|
|
# migrations, Zitadel project/apps, the storage bucket) and stops there. That's
|
|
# the usual dev setup, with the apps running from the IDE. Add `--profile apps
|
|
# up -d --build` as a SECOND command to also build and run them in Docker: by
|
|
# then zitadel-init has already written the OIDC client ids into the env
|
|
# file, so the apps start with real values. See deploy/README.md.
|
|
|
|
# Backend settings that are identical for the API and the Worker. ASP.NET Core
|
|
# maps `__` to `:`, so these override the matching appsettings.json sections.
|
|
# POSTGRES_HOST / RABBITMQ_HOST / JAEGER_HOST differ between environments
|
|
# (host.docker.internal in dev, the container name over infra-net in prod,
|
|
# see .env.example / .env.prod.example), same as PUBLIC_AUTH_URL/S3_PUBLIC_URL
|
|
# already do.
|
|
x-backend-env: &backend-env
|
|
Database__ConnectionString: "Host=${POSTGRES_HOST};Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
|
# Musify's own vhost and user on the shared broker (the path is the vhost).
|
|
MassTransit__Address: "rabbitmq://${RABBITMQ_HOST}:5672/${RABBITMQ_VHOST}"
|
|
MassTransit__Username: ${RABBITMQ_USER}
|
|
MassTransit__Password: ${RABBITMQ_PASSWORD}
|
|
# The public S3 URL, not the internal one: the same address is used to sign
|
|
# the presigned upload/download URLs the browser has to be able to reach.
|
|
InfrastructureStorage__Address: ${S3_PUBLIC_URL}
|
|
InfrastructureStorage__AccessKey: ${S3_ACCESS_KEY}
|
|
InfrastructureStorage__SecretAccessKey: ${S3_SECRET_KEY}
|
|
InfrastructureStorage__ForcePathStyle: "true"
|
|
InfrastructureStorage__UseHttp: ${S3_USE_HTTP}
|
|
ApplicationStorage__Bucket: ${S3_BUCKET}
|
|
OpenTelemetry__OtlpEndpoint: "http://${JAEGER_HOST}:4317"
|
|
|
|
services:
|
|
# --- Shared-infrastructure bootstrap ---------------------------------------
|
|
|
|
# Waits for Infrastructure's Postgres to accept connections before `migrate`
|
|
# runs. This replaces a `depends_on: postgres: condition: service_healthy`:
|
|
# that only works within the same compose file, and Postgres now lives in a
|
|
# separate one (../../../Infrastructure).
|
|
wait-for-postgres:
|
|
image: postgres:17-alpine
|
|
container_name: musify-wait-for-postgres
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
set -e
|
|
deadline=$$(( $$(date +%s) + 60 ))
|
|
until pg_isready -h ${POSTGRES_HOST} -p 5432 -U ${POSTGRES_USER} >/dev/null 2>&1; do
|
|
[ "$$(date +%s)" -lt "$$deadline" ] || { echo "postgres did not respond at ${POSTGRES_HOST}:5432. Is Infrastructure running?" >&2; exit 1; }
|
|
sleep 2
|
|
done
|
|
restart: "no"
|
|
|
|
# One-shot: SeaweedFS does not auto-create buckets. `aws s3 ls` doubles as
|
|
# the readiness probe, since seaweedfs-init no longer has an in-file
|
|
# `depends_on: seaweedfs: condition: service_healthy` to wait for.
|
|
seaweedfs-init:
|
|
image: amazon/aws-cli:latest
|
|
container_name: musify-seaweedfs-init
|
|
environment:
|
|
AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY}
|
|
AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY}
|
|
AWS_DEFAULT_REGION: us-east-1
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
set -e
|
|
deadline=$$(( $$(date +%s) + 60 ))
|
|
until aws --endpoint-url ${S3_PUBLIC_URL} s3 ls >/dev/null 2>&1; do
|
|
[ "$$(date +%s)" -lt "$$deadline" ] || { echo "seaweedfs did not respond at ${S3_PUBLIC_URL}. Is Infrastructure running?" >&2; exit 1; }
|
|
sleep 2
|
|
done
|
|
aws --endpoint-url ${S3_PUBLIC_URL} s3 mb s3://${S3_BUCKET} 2>/dev/null || true
|
|
aws --endpoint-url ${S3_PUBLIC_URL} s3 ls
|
|
restart: "no"
|
|
|
|
# One-shot: generates the RS256 key pair used to sign (API) and verify
|
|
# (Gateway) stream tickets, if it is not already in ./keys. No host tools
|
|
# required, since openssl runs inside the container.
|
|
keys-init:
|
|
image: alpine:3.20
|
|
container_name: musify-keys-init
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
set -e
|
|
if [ -f /keys/stream_private.pem ] && [ -f /keys/stream_public.pem ]; then
|
|
echo "stream-ticket keys already present"
|
|
else
|
|
apk add --no-cache openssl >/dev/null
|
|
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out /keys/stream_private.pem
|
|
openssl rsa -in /keys/stream_private.pem -pubout -out /keys/stream_public.pem
|
|
chmod 644 /keys/stream_public.pem
|
|
chmod 600 /keys/stream_private.pem
|
|
echo "generated stream-ticket keys"
|
|
fi
|
|
volumes:
|
|
- ./keys:/keys
|
|
restart: "no"
|
|
|
|
# One-shot: applies the EF Core migrations, then exits. See
|
|
# backend/Musify.Migrator.Dockerfile.
|
|
migrate:
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.Migrator.Dockerfile
|
|
container_name: musify-migrate
|
|
depends_on:
|
|
wait-for-postgres:
|
|
condition: service_completed_successfully
|
|
environment:
|
|
Database__ConnectionString: "Host=${POSTGRES_HOST};Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}"
|
|
restart: "no"
|
|
|
|
# One-shot: creates the Musify project and its two OIDC apps in the shared
|
|
# Zitadel (or reuses them if they already exist), then writes
|
|
# AUTH_CLIENT_ID, WEB_CLIENT_ID and WEB_CLIENT_SECRET back into the env file
|
|
# mounted at /workspace/.env (see compose.dev.yml / compose.prod.yml), so a
|
|
# later `--profile apps up` picks up real values instead of empty ones.
|
|
zitadel-init:
|
|
image: alpine:3.20
|
|
container_name: musify-zitadel-init
|
|
environment:
|
|
PUBLIC_AUTH_URL: ${PUBLIC_AUTH_URL}
|
|
PUBLIC_API_URL: ${PUBLIC_API_URL}
|
|
PUBLIC_WEB_URL: ${PUBLIC_WEB_URL}
|
|
ZITADEL_DEV_MODE: ${ZITADEL_DEV_MODE:-false}
|
|
volumes:
|
|
- ${ZITADEL_PAT_DIR}:/output:ro
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
set -e
|
|
apk add --no-cache curl jq >/dev/null
|
|
|
|
ENV_FILE=/workspace/.env
|
|
if [ ! -f "$$ENV_FILE" ]; then
|
|
echo "no env file mounted at $$ENV_FILE, skipping"
|
|
exit 0
|
|
fi
|
|
|
|
# The env file may have CRLF line endings (edited on Windows), so
|
|
# strip \r on read and drop it on write, otherwise a "blank" value
|
|
# is actually a lone \r, and `[ -z ... ]` lies.
|
|
env_get() { grep -E "^$$2=" "$$1" 2>/dev/null | tail -n1 | cut -d= -f2- | tr -d '\r'; }
|
|
env_set() {
|
|
# $$1 is a bind-mounted file: rewrite its content in place rather
|
|
# than renaming a temp file over it (rename onto a bind-mounted
|
|
# inode fails with "Resource busy").
|
|
if grep -qE "^$$2=" "$$1"; then
|
|
awk -v k="$$2" -v v="$$3" '{ sub(/\r$$/, "") } index($$0,k "=")==1{print k "=" v; next} {print}' "$$1" >"$$1.tmp" && cat "$$1.tmp" >"$$1" && rm -f "$$1.tmp"
|
|
else
|
|
printf '%s=%s\n' "$$2" "$$3" >>"$$1"
|
|
fi
|
|
}
|
|
|
|
# Musify's own service account, owner of the Musify project only
|
|
# (created by Infrastructure's `provision`, see its README).
|
|
echo "==> waiting for Musify's Zitadel PAT"
|
|
deadline=$$(( $$(date +%s) + 180 ))
|
|
while [ ! -s /output/zitadel.pat ]; do
|
|
[ "$$(date +%s)" -lt "$$deadline" ] || { echo "no zitadel.pat in ZITADEL_PAT_DIR. Is Infrastructure up, and does ZITADEL_PAT_DIR point at its zitadel/.output/musify? Check 'docker logs infra-provision'." >&2; exit 1; }
|
|
sleep 3
|
|
done
|
|
PAT=$$(tr -d '\r\n' </output/zitadel.pat)
|
|
MGMT="$$PUBLIC_AUTH_URL/management/v1"
|
|
|
|
echo "==> waiting for Zitadel to answer at $$PUBLIC_AUTH_URL"
|
|
deadline=$$(( $$(date +%s) + 120 ))
|
|
until curl -fsS "$$PUBLIC_AUTH_URL/.well-known/openid-configuration" >/dev/null 2>&1; do
|
|
[ "$$(date +%s)" -lt "$$deadline" ] || { echo "Zitadel did not respond at $$PUBLIC_AUTH_URL" >&2; exit 1; }
|
|
sleep 3
|
|
done
|
|
|
|
api_call() {
|
|
if [ $$# -ge 3 ]; then
|
|
curl -fsS -X "$$1" "$$MGMT$$2" -H "Authorization: Bearer $$PAT" -H "Content-Type: application/json" -d "$$3"
|
|
else
|
|
curl -fsS -X "$$1" "$$MGMT$$2" -H "Authorization: Bearer $$PAT"
|
|
fi
|
|
}
|
|
name_query() { printf '{"queries":[{"nameQuery":{"name":"%s","method":"TEXT_QUERY_METHOD_EQUALS"}}]}' "$$1"; }
|
|
|
|
echo "==> project"
|
|
PROJECT_NAME=Musify
|
|
# Created by Infrastructure's `provision`: this account can't create
|
|
# projects, only manage this one.
|
|
PROJECT_ID=$$(api_call POST /projects/_search "$$(name_query "$$PROJECT_NAME")" | jq -r --arg n "$$PROJECT_NAME" '.result // [] | map(select(.name==$$n)) | first | .id // empty')
|
|
[ -n "$$PROJECT_ID" ] || { echo "Zitadel project $$PROJECT_NAME not found. Is musify in Infrastructure's x-projects?" >&2; exit 1; }
|
|
echo " found (id $$PROJECT_ID)"
|
|
|
|
find_app() { api_call POST "/projects/$$PROJECT_ID/apps/_search" "$$(name_query "$$1")" | jq -r --arg n "$$1" '.result // [] | map(select(.name==$$n)) | first | .id // empty'; }
|
|
|
|
echo "==> API app"
|
|
API_APP_NAME="Musify API (Scalar)"
|
|
API_APP_ID=$$(find_app "$$API_APP_NAME")
|
|
if [ -n "$$API_APP_ID" ]; then
|
|
API_CLIENT_ID=$$(api_call GET "/projects/$$PROJECT_ID/apps/$$API_APP_ID" | jq -r '.app.oidcConfig.clientId')
|
|
echo " reused (clientId $$API_CLIENT_ID)"
|
|
else
|
|
API_CLIENT_ID=$$(api_call POST "/projects/$$PROJECT_ID/apps/oidc" "$$(jq -n --arg name "$$API_APP_NAME" --arg api "$$PUBLIC_API_URL" '{
|
|
name: $$name,
|
|
redirectUris: [($$api + "/scalar/"), ($$api + "/scalar/oauth2-redirect.html")],
|
|
postLogoutRedirectUris: [($$api + "/scalar/")],
|
|
responseTypes: ["OIDC_RESPONSE_TYPE_CODE"],
|
|
grantTypes: ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE"],
|
|
appType: "OIDC_APP_TYPE_USER_AGENT",
|
|
authMethodType: "OIDC_AUTH_METHOD_TYPE_NONE",
|
|
version: "OIDC_VERSION_1_0",
|
|
devMode: ($$ENV.ZITADEL_DEV_MODE == "true"),
|
|
accessTokenType: "OIDC_TOKEN_TYPE_JWT",
|
|
accessTokenRoleAssertion: true,
|
|
idTokenRoleAssertion: true,
|
|
idTokenUserinfoAssertion: true
|
|
}')" | jq -r '.clientId')
|
|
echo " created (clientId $$API_CLIENT_ID)"
|
|
fi
|
|
|
|
echo "==> web app"
|
|
WEB_APP_NAME="Musify Web"
|
|
WEB_APP_ID=$$(find_app "$$WEB_APP_NAME")
|
|
WEB_CLIENT_SECRET=$$(env_get "$$ENV_FILE" WEB_CLIENT_SECRET)
|
|
if [ -n "$$WEB_APP_ID" ]; then
|
|
WEB_CLIENT_ID=$$(api_call GET "/projects/$$PROJECT_ID/apps/$$WEB_APP_ID" | jq -r '.app.oidcConfig.clientId')
|
|
if [ -z "$$WEB_CLIENT_SECRET" ]; then
|
|
WEB_CLIENT_SECRET=$$(api_call POST "/projects/$$PROJECT_ID/apps/$$WEB_APP_ID/oidc_config/_generate_client_secret" '{}' | jq -r '.clientSecret')
|
|
echo " reused (clientId $$WEB_CLIENT_ID, secret regenerated)"
|
|
else
|
|
echo " reused (clientId $$WEB_CLIENT_ID)"
|
|
fi
|
|
else
|
|
CREATED=$$(api_call POST "/projects/$$PROJECT_ID/apps/oidc" "$$(jq -n --arg name "$$WEB_APP_NAME" --arg web "$$PUBLIC_WEB_URL" '{
|
|
name: $$name,
|
|
redirectUris: [($$web + "/auth/callback")],
|
|
postLogoutRedirectUris: [($$web + "/")],
|
|
responseTypes: ["OIDC_RESPONSE_TYPE_CODE"],
|
|
grantTypes: ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"],
|
|
appType: "OIDC_APP_TYPE_WEB",
|
|
authMethodType: "OIDC_AUTH_METHOD_TYPE_BASIC",
|
|
version: "OIDC_VERSION_1_0",
|
|
devMode: ($$ENV.ZITADEL_DEV_MODE == "true"),
|
|
accessTokenType: "OIDC_TOKEN_TYPE_JWT",
|
|
accessTokenRoleAssertion: true,
|
|
idTokenRoleAssertion: true,
|
|
idTokenUserinfoAssertion: true
|
|
}')")
|
|
WEB_CLIENT_ID=$$(echo "$$CREATED" | jq -r '.clientId')
|
|
WEB_CLIENT_SECRET=$$(echo "$$CREATED" | jq -r '.clientSecret')
|
|
echo " created (clientId $$WEB_CLIENT_ID)"
|
|
fi
|
|
|
|
echo "==> native app (KMP client: desktop loopback + mobile custom scheme)"
|
|
# Loopback (desktop) and a reverse-domain custom scheme (Android/iOS,
|
|
# RFC 8252 §7.1) are both valid for a NATIVE app without devMode, so
|
|
# dev and prod share the same config. Unlike the other apps, an
|
|
# existing one is also updated, so a change here reaches instances
|
|
# created before it.
|
|
NATIVE_APP_NAME="Musify Native"
|
|
NATIVE_CONFIG='{
|
|
"redirectUris": ["http://127.0.0.1:17453/callback", "cat.ikerdemo.musify:/callback"],
|
|
"postLogoutRedirectUris": ["http://127.0.0.1:17453/logout", "cat.ikerdemo.musify:/logout"],
|
|
"responseTypes": ["OIDC_RESPONSE_TYPE_CODE"],
|
|
"grantTypes": ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN"],
|
|
"appType": "OIDC_APP_TYPE_NATIVE",
|
|
"authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE",
|
|
"devMode": false,
|
|
"accessTokenType": "OIDC_TOKEN_TYPE_JWT",
|
|
"accessTokenRoleAssertion": true,
|
|
"idTokenRoleAssertion": true,
|
|
"idTokenUserinfoAssertion": true
|
|
}'
|
|
NATIVE_APP_ID=$$(find_app "$$NATIVE_APP_NAME")
|
|
if [ -n "$$NATIVE_APP_ID" ]; then
|
|
NATIVE_CLIENT_ID=$$(api_call GET "/projects/$$PROJECT_ID/apps/$$NATIVE_APP_ID" | jq -r '.app.oidcConfig.clientId')
|
|
# Zitadel answers 400 "No changes" when the config already matches.
|
|
RESULT=$$(curl -sS -X PUT "$$MGMT/projects/$$PROJECT_ID/apps/$$NATIVE_APP_ID/oidc_config" \
|
|
-H "Authorization: Bearer $$PAT" -H "Content-Type: application/json" -d "$$NATIVE_CONFIG")
|
|
case "$$RESULT" in
|
|
*'"details"'*'"changeDate"'*) echo " reused (clientId $$NATIVE_CLIENT_ID, config updated)" ;;
|
|
*COMMAND-1m88i*) echo " reused (clientId $$NATIVE_CLIENT_ID)" ;;
|
|
*) echo "could not update the native app config: $$RESULT" >&2; exit 1 ;;
|
|
esac
|
|
else
|
|
NATIVE_CLIENT_ID=$$(api_call POST "/projects/$$PROJECT_ID/apps/oidc" "$$(echo "$$NATIVE_CONFIG" | jq --arg name "$$NATIVE_APP_NAME" '. + {name: $$name, version: "OIDC_VERSION_1_0"}')" | jq -r '.clientId')
|
|
echo " created (clientId $$NATIVE_CLIENT_ID)"
|
|
fi
|
|
|
|
env_set "$$ENV_FILE" AUTH_CLIENT_ID "$$API_CLIENT_ID"
|
|
env_set "$$ENV_FILE" WEB_CLIENT_ID "$$WEB_CLIENT_ID"
|
|
env_set "$$ENV_FILE" WEB_CLIENT_SECRET "$$WEB_CLIENT_SECRET"
|
|
env_set "$$ENV_FILE" NATIVE_CLIENT_ID "$$NATIVE_CLIENT_ID"
|
|
echo "==> wired into $$ENV_FILE"
|
|
restart: "no"
|
|
|
|
# --- Applications (profile: apps) -----------------------------------------
|
|
|
|
api:
|
|
profiles: [apps]
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.Api.Dockerfile
|
|
container_name: musify-api
|
|
depends_on:
|
|
migrate:
|
|
condition: service_completed_successfully
|
|
keys-init:
|
|
condition: service_completed_successfully
|
|
environment:
|
|
<<: *backend-env
|
|
ASPNETCORE_ENVIRONMENT: ${DOTNET_ENVIRONMENT}
|
|
ASPNETCORE_URLS: http://+:5111
|
|
StreamGateway__PublicBaseUrl: ${PUBLIC_STREAM_URL}
|
|
StreamTicket__PrivateKeyPath: /keys/stream_private.pem
|
|
# AUTH_CLIENT_ID is written to the env file by the zitadel-init service.
|
|
Authentication__MetadataAddress: "${PUBLIC_AUTH_URL}/.well-known/openid-configuration"
|
|
Authentication__IssuerAddress: ${PUBLIC_AUTH_URL}
|
|
Authentication__ClientId: ${AUTH_CLIENT_ID}
|
|
Authentication__AudienceAddress: ${AUTH_CLIENT_ID}
|
|
Authentication__AuthorizationEndpoint: "${PUBLIC_AUTH_URL}/oauth/v2/authorize?prompt=login"
|
|
Authentication__TokenEndpoint: "${PUBLIC_AUTH_URL}/oauth/v2/token"
|
|
Authentication__ScalarRedirectUri: "${PUBLIC_API_URL}/scalar/"
|
|
Authentication__Scopes__0: openid
|
|
Authentication__Scopes__1: profile
|
|
Authentication__Scopes__2: email
|
|
Authentication__RequireHttpsMetadata: ${AUTH_REQUIRE_HTTPS}
|
|
volumes:
|
|
- ./keys:/keys:ro
|
|
restart: unless-stopped
|
|
|
|
worker:
|
|
profiles: [apps]
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.Worker.Dockerfile
|
|
container_name: musify-worker
|
|
depends_on:
|
|
migrate:
|
|
condition: service_completed_successfully
|
|
keys-init:
|
|
condition: service_completed_successfully
|
|
environment:
|
|
<<: *backend-env
|
|
DOTNET_ENVIRONMENT: ${DOTNET_ENVIRONMENT}
|
|
# The Application handlers need these to build their dependencies; the
|
|
# Worker never issues stream tickets, so the key is never read.
|
|
StreamGateway__PublicBaseUrl: ${PUBLIC_STREAM_URL}
|
|
StreamTicket__PrivateKeyPath: /keys/stream_private.pem
|
|
Workers__Routes__TemporaryFilesDirectory: /tmp/musify
|
|
volumes:
|
|
- ./keys:/keys:ro
|
|
restart: unless-stopped
|
|
|
|
# YARP in front of the SeaweedFS filer: validates the RS256 stream ticket and
|
|
# streams the bytes, so audio never goes through the API.
|
|
gateway:
|
|
profiles: [apps]
|
|
build:
|
|
context: ../backend
|
|
dockerfile: Musify.StreamingGateway.Dockerfile
|
|
container_name: musify-gateway
|
|
depends_on:
|
|
keys-init:
|
|
condition: service_completed_successfully
|
|
environment:
|
|
ASPNETCORE_ENVIRONMENT: ${DOTNET_ENVIRONMENT}
|
|
ASPNETCORE_URLS: http://+:8081
|
|
StreamTicket__PublicKeyPath: /keys/stream_public.pem
|
|
ReverseProxy__Clusters__seaweed-filer__Destinations__filer__Address: "http://${SEAWEEDFS_HOST}:8888/"
|
|
ReverseProxy__Routes__media__Transforms__0__PathPattern: "/buckets/${S3_BUCKET}/{**rest}"
|
|
Cors__AllowedOrigins__0: ${PUBLIC_WEB_URL}
|
|
volumes:
|
|
- ./keys:/keys:ro
|
|
restart: unless-stopped
|
|
|
|
web-player:
|
|
profiles: [apps]
|
|
build:
|
|
context: ../web-player
|
|
dockerfile: Dockerfile
|
|
container_name: musify-web-player
|
|
depends_on:
|
|
- api
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: 3000
|
|
# adapter-node rejects request bodies over 512K by default; audio
|
|
# uploads are posted to /upload. Keep in line with the edge's
|
|
# client_max_body_size.
|
|
BODY_SIZE_LIMIT: 1G
|
|
ORIGIN: ${PUBLIC_WEB_URL}
|
|
ZITADEL_ISSUER: ${PUBLIC_AUTH_URL}
|
|
ZITADEL_CLIENT_ID: ${WEB_CLIENT_ID}
|
|
ZITADEL_CLIENT_SECRET: ${WEB_CLIENT_SECRET}
|
|
AUTH_REDIRECT_URI: "${PUBLIC_WEB_URL}/auth/callback"
|
|
AUTH_POST_LOGOUT_URI: "${PUBLIC_WEB_URL}/"
|
|
AUTH_ALLOW_INSECURE_HTTP: ${AUTH_ALLOW_INSECURE_HTTP}
|
|
SESSION_SECRET: ${SESSION_SECRET}
|
|
# The container name, not the `api` service name: in prod every project
|
|
# shares infra-net, where a generic name can resolve to another
|
|
# project's container.
|
|
API_BASE_URL: http://musify-api:5111
|
|
# Presigned PUTs are signed for the public S3 host; the container sends
|
|
# them straight to SeaweedFS instead of going back out through the edge.
|
|
S3_UPLOAD_ENDPOINT: ${S3_INTERNAL_URL}
|
|
restart: unless-stopped
|