Files
Musify/docs/development.md
T
Admin cf8ac706b6 Replace deploy scripts with self-contained compose bootstrap jobs
Removes every wrapper script under deploy/ (up.ps1, up.sh, scripts/*,
zitadel/provision.*) and folds their logic into three one-shot services
in compose.yml, following the pattern already used by seaweedfs-init:

  keys-init     generates the RS256 stream-ticket key pair
  migrate       applies the EF Core migrations (backend/Musify.Migrator.Dockerfile)
  zitadel-init  creates the Musify project + OIDC apps, writes the client
                ids back into the active env file

Deploying is now two plain `docker compose` commands, no scripts to run
or chmod:

  docker compose -f deploy/compose.yml -f deploy/compose.dev.yml up -d
  docker compose -f deploy/compose.yml -f deploy/compose.dev.yml --profile apps up -d --build

(swap compose.dev.yml for compose.prod.yml + --env-file deploy/.env.prod
for production). The first command brings up infra and lets the bootstrap
jobs run to completion; the second builds and starts the apps, which by
then read real OIDC values instead of empty ones — compose interpolates
${VAR} once per invocation, so this can't collapse into a single command
without the apps reading their own config file at container start instead
of via compose env (out of scope here).

Also:
- DatabaseDesignTimeFactory now also reads Database__ConnectionString
  from the environment, and DesignSettings.json is optional, so the new
  migrate container works without user-secrets or a config file excluded
  from the Docker build context by .dockerignore.
- nginx's depends_on no longer lists the apps-profile services: it does
  not need them at its own startup (every vhost resolves its upstream at
  request time via the embedded Docker resolver), and requiring them made
  `compose.prod.yml up -d` (without --profile apps) an invalid project.
- env_get/env_set in zitadel-init strip \r: the mounted env file can have
  CRLF line endings on Windows, which otherwise makes an empty-looking
  value non-empty to `[ -z ... ]`. .env.example/.env.prod.example are
  pinned to LF via .gitattributes for the same reason. Also fixed:
  rewriting the bind-mounted env file in place instead of renaming a temp
  file over it (rename onto a bind-mounted inode fails with "Resource
  busy").
- Removed compose.share.yml and zitadel/.output-share/ — a "temporary"
  personal overlay that had leaked a real Zitadel service-account PAT
  into git history (committed in 70968b3). That token should be rotated.
- Removed deploy/.tmp/ from .gitignore: nothing mounts or creates it
  anymore (it never actually did, either — no compose service referenced
  it, only the now-removed up.ps1 did).

Verified end-to-end with Docker: a clean `down -v` + fresh `up -d` +
`--profile apps up -d --build` on the dev stack brings up all four one-shot
jobs successfully and all four apps healthy (api/gateway return 200,
worker starts its consumers with no errors, web-player responds). Also
verified compose.prod.yml starts nginx cleanly with dummy certs and no
apps running, confirming the depends_on fix.
2026-09-09 15:08:34 +02:00

3.7 KiB

Entorno de desarrollo

Cómo levantar todo en local y probarlo. El detalle completo del deploy (y el de producción) está en deploy/README.md.

Arranque

docker compose -f deploy/compose.yml -f deploy/compose.dev.yml up -d

Ese único comando levanta la infraestructura y corre los jobs one-shot de bootstrap: genera las claves RS256 de stream-ticket en deploy/keys/, aplica las migraciones EF Core y provisiona Zitadel (proyecto + apps OIDC), dejando los client ids en deploy/.env. Detalle completo, incluido cómo arrancar las apps en Docker con --profile apps, en deploy/README.md.

Aplicaciones

dotnet run --project backend/Hosts/Musify.Api               # :5111
dotnet run --project backend/Hosts/Musify.StreamingGateway  # :8081
dotnet run --project backend/Hosts/Musify.Worker            # background
npm --prefix web-player run dev                       # :5173

Mapa de puertos

Servicio Puerto
Musify.Api 5111 (/scalar/v1, /openapi/v1.json)
Musify.StreamingGateway 8081 (/health, /media/...)
web-player 5173 (npm run dev) o 3000 (contenedor)
SeaweedFS S3 / filer / master 8333 / 8888 / 9333
Zitadel 8080
PostgreSQL 59000 (→ 5432 en el contenedor)
RabbitMQ AMQP / management 5672 / 15672
Jaeger UI / OTLP 16686 / 4317
pgAdmin (-Tools) 5050

DB: musify_db, usuario postgres/postgres. Bucket S3: webapi-storage (credenciales admin_access_key/admin_secret_key). Todos los valores salen de deploy/.env.

Migraciones

El servicio migrate de deploy/compose.yml las aplica en cada docker compose up -d (no-op si el esquema ya está al día). A mano, tras cambios de esquema, contra la base local:

dotnet ef database update --project backend/Core/Musify.Infrastructure --startup-project backend/Core/Musify.Infrastructure --context Database

Musify.Infrastructure es a la vez el proyecto de migraciones y el de arranque: tiene el IDesignTimeDbContextFactory y el paquete EFCore.Design. La cadena de conexión se lee de sus user-secrets, o de la variable de entorno Database__ConnectionString si no hay user-secrets (así es como la usa el contenedor migrate, ver backend/Musify.Migrator.Dockerfile).

Stream-tickets (claves RS256)

La API firma los stream-tickets con la clave privada y el gateway los valida con la pública. El servicio keys-init de deploy/compose.yml las genera en deploy/keys/ si no existen; a mano:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out deploy/keys/stream_private.pem
openssl rsa -in deploy/keys/stream_private.pem -pubout -out deploy/keys/stream_public.pem
  • API: StreamTicket:PrivateKeyPath.
  • Gateway: StreamTicket:PublicKeyPath.

Config (AppSettings)

Convención: AppSettings.json es la plantilla con valores vacíos o neutros; AppSettings.Development.json tiene los valores reales de dev; en Docker todo se sobreescribe con variables de entorno (Seccion__Clave) desde deploy/compose.yml. Para correr Musify.Api desde el IDE, los valores de Authentication van en user-secrets (no en el repo) — cópialos de deploy/.env una vez que zitadel-init los haya generado (ver deploy/README.md).

Secciones clave: Authentication (Zitadel), Database, MassTransit, InfrastructureStorage (S3), ApplicationStorage (bucket), Track, PlayList, UploadIntent, AudioTranscoder, YtDlp, Workers, StreamGateway / StreamTicket.

Requisitos del host

  • .NET 10 SDK y Node 22.
  • ffmpeg y yt-dlp en el PATH (transcode y descarga de audio del Worker).
  • Docker Desktop.