docs: rewrite the deploy documentation and fix the stale references
The root README was a design handoff for a prototype that is not in this repo, web-player/README.md was the sv template, and deploy/README.md only covered development. Replace all three and document the production deploy: its prerequisites, what up.sh does and why nginx sits in front. The docs under docs/ still used the pre-monorepo project paths, Ardalis.Result instead of ErrorOr and the player.html test console that no longer exists.
This commit is contained in:
+8
-6
@@ -13,12 +13,13 @@ Esta carpeta explica **qué hace cada pieza, su propósito y por qué está dise
|
||||
- [streaming.md](streaming.md) — Streaming del audio (`.m4a` por HTTP range) con stream-ticket (RS256) y el reverse proxy (StreamingGateway).
|
||||
- [media-processing.md](media-processing.md) — El worker y los workflows de MassTransit (transcode de audio, miniaturas).
|
||||
- [development.md](development.md) — Cómo levantar todo en local: contenedores, puertos y comandos.
|
||||
- [../deploy/README.md](../deploy/README.md) — El deploy en detalle: compose, scripts y producción.
|
||||
|
||||
## Mapa de componentes (alto nivel)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
Cliente ───► │ WebApi (:5111) API REST + emite tickets │
|
||||
Cliente ───► │ Musify.Api (:5111) API REST + emite tickets │
|
||||
└─────────────────────────────────────────────┘
|
||||
│ EF Core │ presigned (subidas) │ MassTransit (eventos)
|
||||
▼ ▼ ▼
|
||||
@@ -27,19 +28,20 @@ Esta carpeta explica **qué hace cada pieza, su propósito y por qué está dise
|
||||
▲
|
||||
Cliente ───► StreamingGateway (:8081) ┘ sirve audio .m4a (valida ticket, proxy al filer)
|
||||
|
||||
Login: Cliente ◄──► Zitadel (:8080, OIDC) → WebApi valida el JWT
|
||||
Login: Cliente ◄──► Zitadel (:8080, OIDC) → Musify.Api valida el JWT
|
||||
```
|
||||
|
||||
## Stack
|
||||
|
||||
- **.NET 10**, C#. Minimal APIs (WebApi), Worker Service (Worker), ASP.NET + YARP (StreamingGateway).
|
||||
- **CQRS** con [Mediator] (source-generator) y resultados con **Ardalis.Result**.
|
||||
- **.NET 10**, C#. Minimal APIs (`Musify.Api`), Worker Service (`Musify.Worker`), ASP.NET + YARP (`Musify.StreamingGateway`).
|
||||
- **CQRS** con [Mediator] (source-generator) y resultados con **ErrorOr**.
|
||||
- **EF Core 10** + **PostgreSQL** (metadatos).
|
||||
- **MassTransit 8** + **RabbitMQ** (procesado asíncrono con routing slips/sagas).
|
||||
- **SeaweedFS** (almacenamiento de objetos compatible con S3, vía AWSSDK.S3).
|
||||
- **ffmpeg** (transcode de audio a AAC/`.m4a`), **SixLabors.ImageSharp** (miniaturas).
|
||||
- **Zitadel** (OIDC/OAuth2) para identidad.
|
||||
|
||||
## Repositorios git
|
||||
## Estructura del repositorio
|
||||
|
||||
Cada proyecto de nivel superior es su propio repo git: `MusifyBackend`, `Musify.WebApi`, `Musify.StreamingGateway`, `Musify.Worker`. La raíz del workspace (`docs/`, `deploy/`, `player.html`) no está versionada.
|
||||
Un solo repo: `backend/` (los seis proyectos .NET), `web-player/` (cliente web
|
||||
SvelteKit), `deploy/` (stacks de Docker Compose y scripts) y `docs/`.
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
## Principios
|
||||
|
||||
- **Clean architecture / capas** en `MusifyBackend`: el dominio y la aplicación no dependen de infraestructura; la infraestructura implementa las interfaces (`Application/Contracts`).
|
||||
- **CQRS**: cada operación es un `Command` o `Query` con su handler (Mediator). Los handlers devuelven `Ardalis.Result` (Ok/NotFound/Unauthorized/Conflict/Invalid…), que la WebApi traduce a códigos HTTP.
|
||||
- **Los bytes pesados no pasan por la API**: subidas con URLs prefirmadas (cliente → S3) y reproducción vía un reverse proxy dedicado (gateway → S3). La WebApi solo mueve metadatos y emite permisos.
|
||||
- **Clean architecture / capas** en `backend/`: el dominio y la aplicación no dependen de infraestructura; la infraestructura implementa las interfaces (`Application/Contracts`).
|
||||
- **CQRS**: cada operación es un `Command` o `Query` con su handler (Mediator). Los handlers devuelven `Ardalis.Result` (Ok/NotFound/Unauthorized/Conflict/Invalid…), que la API traduce a códigos HTTP.
|
||||
- **Los bytes pesados no pasan por la API**: subidas con URLs prefirmadas (cliente → S3) y reproducción vía un reverse proxy dedicado (gateway → S3). La API solo mueve metadatos y emite permisos.
|
||||
- **Procesado asíncrono**: crear una pista no transcodifica en caliente; publica un evento y un worker hace el trabajo pesado con compensación (sagas/routing slips de MassTransit).
|
||||
|
||||
## Componentes
|
||||
|
||||
| Componente | Responsabilidad | Por qué separado |
|
||||
|---|---|---|
|
||||
| **WebApi** | API REST (usuarios, pistas, playlists), autoriza, emite presigned URLs y stream-tickets | Punto de entrada fino; no procesa medios ni sirve bytes |
|
||||
| **MusifyBackend** | Dominio + casos de uso + infraestructura compartida (EF, S3, MassTransit, ffmpeg) | Lógica reutilizada por WebApi y Worker |
|
||||
| **Musify.Api** | API REST (usuarios, pistas, playlists), autoriza, emite presigned URLs y stream-tickets | Punto de entrada fino; no procesa medios ni sirve bytes |
|
||||
| **Domain/Application/Infrastructure** | Dominio + casos de uso + infraestructura compartida (EF, S3, MassTransit, ffmpeg) | Lógica reutilizada por la API y el Worker |
|
||||
| **StreamingGateway** | Reverse proxy (YARP) que valida el ticket y sirve el audio (`.m4a`) desde SeaweedFS | Escala con el tráfico de bytes, independiente de la API; SeaweedFS queda privado detrás |
|
||||
| **Worker** | Consume eventos de MassTransit: transcodifica audio a `.m4a` y genera miniaturas | Trabajo CPU/IO intensivo fuera del request HTTP |
|
||||
| **PostgreSQL** | Metadatos (usuarios, pistas, playlists, upload intents) | — |
|
||||
@@ -23,15 +23,15 @@
|
||||
## Flujos principales
|
||||
|
||||
### 1. Subida de una pista
|
||||
1. Cliente pide `POST /tracks/upload-urls` → la WebApi crea **UploadIntents** (reservas) y devuelve **URLs prefirmadas PUT** (imagen + audio).
|
||||
1. Cliente pide `POST /tracks/upload-urls` → la API crea **UploadIntents** (reservas) y devuelve **URLs prefirmadas PUT** (imagen + audio).
|
||||
2. Cliente sube los ficheros **directo a SeaweedFS** con esas URLs (no pasan por la API).
|
||||
3. Cliente llama `POST /tracks` con los `intentId`s → la WebApi valida los intents, crea el `Track` (+ `UserHasTrack`) y publica `CreateTrackResourcesEvent`.
|
||||
3. Cliente llama `POST /tracks` con los `intentId`s → la API valida los intents, crea el `Track` (+ `UserHasTrack`) y publica `CreateTrackResourcesEvent`.
|
||||
4. El **Worker** consume el evento: mueve los originales a su sitio, genera miniaturas y transcodifica el audio a `.m4a`, y actualiza el estado del track.
|
||||
|
||||
Ver detalle en [media-processing.md](media-processing.md) y [storage.md](storage.md).
|
||||
|
||||
### 2. Reproducción (streaming)
|
||||
1. Cliente pide `GET /tracks/{id}/stream` (autenticado) → la WebApi autoriza y devuelve `{ manifestUrl, ticket }` (URL al `.m4a` + ticket RS256 acotado a la carpeta de esa pista).
|
||||
1. Cliente pide `GET /tracks/{id}/stream` (autenticado) → la API autoriza y devuelve `{ manifestUrl, ticket }` (URL al `.m4a` + ticket RS256 acotado a la carpeta de esa pista).
|
||||
2. El reproductor abre la URL del audio añadiendo `?t=<ticket>` (o cabecera `X-Stream-Ticket`) y descarga por HTTP range.
|
||||
3. El **StreamingGateway** valida el ticket y reenvía al filer de SeaweedFS; los bytes van storage → cliente.
|
||||
|
||||
@@ -39,7 +39,7 @@ Ver [streaming.md](streaming.md).
|
||||
|
||||
### 3. Autenticación
|
||||
1. Cliente hace login OIDC contra **Zitadel** y obtiene un access token (JWT).
|
||||
2. La WebApi valida la firma/iss/aud del JWT.
|
||||
2. La Musify.Api valida la firma/iss/aud del JWT.
|
||||
3. En cada token validado, se **sincroniza** el usuario local (crea/actualiza nombre y foto) — provisioning just-in-time.
|
||||
|
||||
Ver [authentication.md](authentication.md).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Proveedor: Zitadel (OIDC/OAuth2)
|
||||
|
||||
El login y la emisión de tokens los hace **Zitadel** (no la WebApi). La WebApi es un **resource server**: solo **valida** el access token (JWT) que le llega en `Authorization: Bearer`.
|
||||
El login y la emisión de tokens los hace **Zitadel** (no la API). La API es un **resource server**: solo **valida** el access token (JWT) que le llega en `Authorization: Bearer`.
|
||||
|
||||
- Config en la sección `Authentication` (`AuthenticationConfiguration`): metadata address, issuer, audience, client id, endpoints de authorize/token, scopes.
|
||||
- `JwtBearerOptionsSetup` configura la validación (issuer, audience, claves vía metadata OIDC).
|
||||
|
||||
+48
-50
@@ -1,92 +1,90 @@
|
||||
# Entorno de desarrollo
|
||||
|
||||
Cómo levantar todo en local y probarlo.
|
||||
Cómo levantar todo en local y probarlo. El detalle completo del deploy (y el de
|
||||
producción) está en [deploy/README.md](../deploy/README.md).
|
||||
|
||||
## Dependencias (Docker)
|
||||
|
||||
Todo (PostgreSQL, Zitadel, RabbitMQ, SeaweedFS, Jaeger) vive en un solo stack en
|
||||
[`deploy/`](../deploy/README.md). Bootstrap completo (levanta + bucket + claves + migraciones):
|
||||
## Arranque
|
||||
|
||||
```powershell
|
||||
./deploy/up.ps1
|
||||
./deploy/up.ps1 # infraestructura; las apps se ejecutan desde el IDE
|
||||
./deploy/up.ps1 -Apps # todo en Docker (api, worker, gateway, web player)
|
||||
```
|
||||
|
||||
O solo la infraestructura:
|
||||
`up.ps1` hace el bootstrap completo: crea `deploy/.env`, genera las claves RS256
|
||||
de stream-ticket en `deploy/keys/`, levanta el stack, aplica las migraciones y
|
||||
provisiona Zitadel (proyecto + apps OIDC), dejando los client ids en
|
||||
`deploy/.env`, en los user-secrets de `Musify.Api` y en `web-player/.env`.
|
||||
|
||||
Otros modificadores: `-Tools` (pgAdmin), `-SkipMigrations`, `-Down`, `-Destroy`.
|
||||
|
||||
## Aplicaciones
|
||||
|
||||
```powershell
|
||||
docker compose -f deploy\docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Config (usuarios/claves/puertos) en `deploy/.env`. Ver [deploy/README.md](../deploy/README.md)
|
||||
para el detalle y el paso de configuración de Zitadel (crear la app OIDC y copiar el ClientId).
|
||||
|
||||
## Aplicaciones (.NET)
|
||||
|
||||
```powershell
|
||||
dotnet run --project Musify.WebApi\WebApi # :5111
|
||||
dotnet run --project Musify.StreamingGateway\StreamingGateway # :8081
|
||||
dotnet run --project Musify.Worker\Worker # background
|
||||
dotnet run --project backend/Musify.Api # :5111
|
||||
dotnet run --project backend/Musify.StreamingGateway # :8081
|
||||
dotnet run --project backend/Musify.Worker # background
|
||||
npm --prefix web-player run dev # :5173
|
||||
```
|
||||
|
||||
## Mapa de puertos
|
||||
|
||||
| Servicio | Puerto |
|
||||
|---|---|
|
||||
| WebApi | 5111 (`/scalar/v1`, `/openapi/v1.json`) |
|
||||
| StreamingGateway | 8081 (`/health`, `/media/...`) |
|
||||
| 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 |
|
||||
| pgAdmin | 5050 |
|
||||
| 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`).
|
||||
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
|
||||
|
||||
No hay migración automática al arrancar. `deploy/up.ps1` las aplica en el bootstrap;
|
||||
para hacerlo a mano tras cambios de esquema:
|
||||
No hay migración automática al arrancar; `up.ps1` las aplica en el bootstrap.
|
||||
A mano, tras cambios de esquema:
|
||||
|
||||
```powershell
|
||||
dotnet ef database update --project backend\Musify.Infrastructure --startup-project backend\Musify.Infrastructure --context Database
|
||||
dotnet ef database update --project backend/Musify.Infrastructure --startup-project backend/Musify.Infrastructure --context Database
|
||||
```
|
||||
|
||||
La cadena de conexión para `dotnet ef` se lee de `DesignSettings.json` / user-secrets del proyecto Infrastructure
|
||||
(`up.ps1` la deja en user-secrets automáticamente).
|
||||
`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 (`up.ps1` la deja puesta).
|
||||
|
||||
## Stream-tickets (claves RS256)
|
||||
|
||||
La WebApi firma los stream-tickets con una clave privada y el gateway valida con la pública:
|
||||
La API firma los stream-tickets con la clave privada y el gateway los valida con
|
||||
la pública. `up.ps1` las genera en `deploy/keys/`; a mano:
|
||||
|
||||
```powershell
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out stream_private.pem
|
||||
openssl rsa -in stream_private.pem -pubout -out stream_public.pem
|
||||
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
|
||||
```
|
||||
|
||||
- WebApi: `StreamTicket:PrivateKeyPath` → `stream_private.pem`.
|
||||
- Gateway: `StreamTicket:PublicKeyPath` → `stream_public.pem`.
|
||||
|
||||
## Consola de pruebas: `player.html`
|
||||
|
||||
Un único archivo HTML para probar todo sin front:
|
||||
|
||||
1. Sírvelo o ábrelo directo (el CORS está abierto en Development).
|
||||
2. Pega tu **access token** de Zitadel y la API base (`http://localhost:5111`).
|
||||
3. Permite: crear/actualizar/eliminar playlists, listar canciones, añadir/quitar canciones de una playlist, ver las canciones de una playlist y **reproducir** (pide el ticket y abre el `.m4a` con `?t=<ticket>`).
|
||||
|
||||
> Nota: `player.html` todavía usa dash.js; tras el cambio a `.m4a` debe actualizarse para reproducir el fichero directo (elemento `<audio>` por HTTP range).
|
||||
|
||||
Para obtener un token: login OAuth2 en Scalar (`/scalar/v1`) o cópialo de DevTools.
|
||||
- API: `StreamTicket:PrivateKeyPath`.
|
||||
- Gateway: `StreamTicket:PublicKeyPath`.
|
||||
|
||||
## Config (AppSettings)
|
||||
|
||||
Convención: `AppSettings.json` = plantilla con valores vacíos/por defecto; `AppSettings.Development.json` = valores reales de dev. Secretos sensibles (claves, connection strings) van idealmente en user-secrets.
|
||||
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`. Los valores de `Authentication` los deja el provisioning
|
||||
en user-secrets, no en el repo.
|
||||
|
||||
Secciones clave: `Authentication` (Zitadel), `Database`, `MassTransit`, `InfrastructureStorage` (S3), `ApplicationStorage` (bucket), `Track`, `PlayList`, `UploadIntent`, `AudioTranscoder`, `Workers`, `StreamGateway`/`StreamTicket`.
|
||||
Secciones clave: `Authentication` (Zitadel), `Database`, `MassTransit`,
|
||||
`InfrastructureStorage` (S3), `ApplicationStorage` (bucket), `Track`, `PlayList`,
|
||||
`UploadIntent`, `AudioTranscoder`, `YtDlp`, `Workers`, `StreamGateway` /
|
||||
`StreamTicket`.
|
||||
|
||||
## Requisitos del host
|
||||
|
||||
- .NET 10 SDK.
|
||||
- **ffmpeg** en el PATH (transcode de audio del Worker).
|
||||
- .NET 10 SDK y Node 22.
|
||||
- **ffmpeg** y **yt-dlp** en el PATH (transcode y descarga de audio del Worker).
|
||||
- Docker Desktop.
|
||||
- Directorio temporal de trabajo del Worker (`Workers:Routes:TemporaryFilesDirectory`, p. ej. `D:\tempsFilesDev`).
|
||||
|
||||
+18
-11
@@ -1,8 +1,9 @@
|
||||
# Proyectos
|
||||
|
||||
Cada carpeta de nivel superior es una solución/repo independiente.
|
||||
Los seis proyectos .NET viven en `backend/` (solución `Musify.slnx`); el cliente
|
||||
web en `web-player/`.
|
||||
|
||||
## Musify.WebApi/WebApi (`:5111`)
|
||||
## Musify.Api (`:5111`)
|
||||
|
||||
API REST pública (Minimal APIs, .NET 10). Es la fachada fina del sistema.
|
||||
|
||||
@@ -11,12 +12,12 @@ API REST pública (Minimal APIs, .NET 10). Es la fachada fina del sistema.
|
||||
- **`CurrentUser`**: abstracción que se enlaza como parámetro de endpoint (`IBindableFromHttpContext`) y expone los claims básicos (`Id` como `long?`, `RequiredId`, nombre, etc.) sin tener que leer `ClaimsPrincipal` a mano.
|
||||
- **Emite**: presigned URLs de subida (vía `IStorageService`) y **stream-tickets** (vía `IStreamTicketService`, RS256).
|
||||
- **OpenAPI + Scalar** para documentación/prueba; OAuth2 (PKCE) configurado para login desde Scalar.
|
||||
- **CORS** totalmente abierto **solo en Development** (para el `player.html` y front local).
|
||||
- Traduce `Ardalis.Result` → HTTP en `ResultHttpExtensions`.
|
||||
- **CORS** totalmente abierto **solo en Development** (para el front local).
|
||||
- Traduce `ErrorOr` → HTTP en `ResultHttpExtensions`.
|
||||
|
||||
**Propósito/porqué**: separar la cara pública (autorización + metadatos) del procesado y del servido de bytes.
|
||||
|
||||
## MusifyBackend (Domain + Application + Infrastructure)
|
||||
## Musify.Domain + Musify.Application + Musify.Infrastructure
|
||||
|
||||
El núcleo, en tres proyectos (clean architecture):
|
||||
|
||||
@@ -31,7 +32,7 @@ Casos de uso (CQRS con Mediator):
|
||||
- `Shared/` → helpers transversales (`StorageKey.Combine`, `ImageSize`).
|
||||
- `Pagination/` → `PaginatedResponse<T>`.
|
||||
|
||||
**Porqué**: la lógica de negocio no conoce EF, S3 ni RabbitMQ; solo interfaces. Reutilizable por WebApi y Worker.
|
||||
**Porqué**: la lógica de negocio no conoce EF, S3 ni RabbitMQ; solo interfaces. Reutilizable por la API y el Worker.
|
||||
|
||||
### Infrastructure
|
||||
Implementaciones concretas:
|
||||
@@ -40,20 +41,26 @@ Implementaciones concretas:
|
||||
- `MassTransit/` → consumers, activities y routing slips (ver [media-processing.md](media-processing.md)).
|
||||
- `Jobs/` → `UploadIntentExpirationJob`, `TemporalUploadsCleanUpJob`.
|
||||
|
||||
## Musify.StreamingGateway/StreamingGateway (`:8081`)
|
||||
## Musify.StreamingGateway (`:8081`)
|
||||
|
||||
Reverse proxy (ASP.NET + **YARP**) que sirve el audio (`.m4a`) desde SeaweedFS. Valida el stream-ticket (RS256, clave pública) y, si el objeto pedido cae bajo el prefijo autorizado, reenvía al filer.
|
||||
|
||||
**Porqué un proyecto aparte**: escala con el tráfico de bytes independientemente de la API, tiene superficie mínima (solo sabe validar un JWT y proxiar) y permite que SeaweedFS quede en red privada. Ver [streaming.md](streaming.md).
|
||||
|
||||
## Musify.Worker/Worker
|
||||
## Musify.Worker
|
||||
|
||||
Worker Service (host de fondo) que consume los eventos de MassTransit y ejecuta el procesado pesado: transcode de audio a `.m4a` (ffmpeg), generación de miniaturas (ImageSharp), transferencias de ficheros en el bucket, y los jobs de expiración/limpieza de upload intents.
|
||||
|
||||
**Porqué aparte**: el trabajo CPU/IO no debe ocurrir dentro de un request HTTP; se escala y reinicia por separado. (Antes se llamaba `Musify.PictureWorker`; se renombró porque hace más que imágenes.)
|
||||
|
||||
## Raíz del workspace (no versionada)
|
||||
## web-player (`:5173` en dev, `:3000` en contenedor)
|
||||
|
||||
- **`deploy/seaweedfs/`** — `docker-compose.yml` + `s3.conf` para levantar SeaweedFS.
|
||||
- **`player.html`** — consola de pruebas (un solo archivo): login con token, CRUD de playlists, añadir/quitar canciones, listar y reproducir. Ver [development.md](development.md). (Pendiente: hoy usa dash.js; con el cambio a `.m4a` debe reproducir el fichero directo por `<audio>`/range.)
|
||||
Cliente web (SvelteKit 2 + Svelte 5 + Tailwind 4). La sesión OIDC vive en el
|
||||
servidor (cookie cifrada); las llamadas al backend salen del servidor con el
|
||||
access token, y las subidas se hacen contra las URLs prefirmadas de S3.
|
||||
|
||||
## Otras carpetas
|
||||
|
||||
- **`deploy/`** — stacks de Docker Compose (dev y prod) y los scripts de
|
||||
arranque, migraciones y provisioning. Ver [../deploy/README.md](../deploy/README.md).
|
||||
- **`docs/`** — esta documentación.
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ Expone:
|
||||
- **filer HTTP** (`:8888`) — sirve los ficheros por ruta; lo usa el **StreamingGateway**.
|
||||
- master (`:9333`) y volume server — internos.
|
||||
|
||||
Bucket de la app: **`webapi-storage`** (`ApplicationStorage:Bucket`). Importante: la WebApi y el Worker usan **el mismo bucket** (el worker procesa lo que sube la API).
|
||||
Bucket de la app: **`webapi-storage`** (`ApplicationStorage:Bucket`). Importante: la API y el Worker usan **el mismo bucket** (el worker procesa lo que sube la API).
|
||||
|
||||
## Estructura de claves (layout)
|
||||
|
||||
|
||||
+5
-5
@@ -2,7 +2,7 @@
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reproducir el audio **sin que los bytes pasen por la WebApi**, pero con control de acceso. La WebApi solo emite un permiso corto; el reverse proxy sirve los bytes desde SeaweedFS.
|
||||
Reproducir el audio **sin que los bytes pasen por la API**, pero con control de acceso. La API solo emite un permiso corto; el reverse proxy sirve los bytes desde SeaweedFS.
|
||||
|
||||
## Formato: fichero único AAC (.m4a) por HTTP range
|
||||
|
||||
@@ -16,7 +16,7 @@ Se autoriza **el prefijo de la carpeta** de la pista con un *stream-ticket* de c
|
||||
|
||||
## Flujo
|
||||
|
||||
1. `GET /tracks/{id}/stream` (autenticado) → la WebApi autoriza y devuelve:
|
||||
1. `GET /tracks/{id}/stream` (autenticado) → la API autoriza y devuelve:
|
||||
```json
|
||||
{ "manifestUrl": "http://<gateway>/media/Tracks/ProcessedAudios/<folder>/audio.m4a",
|
||||
"ticket": "<JWT RS256>", "expiresInSeconds": 3600 }
|
||||
@@ -27,10 +27,10 @@ Se autoriza **el prefijo de la carpeta** de la pista con un *stream-ticket* de c
|
||||
|
||||
## El ticket (RS256)
|
||||
|
||||
- Lo firma la WebApi con clave **privada** (`StreamTicketService`); el gateway valida con la **pública** (`TicketValidator`). Así el gateway **no puede emitir** tickets, solo verificarlos.
|
||||
- Lo firma la API con clave **privada** (`StreamTicketService`); el gateway valida con la **pública** (`TicketValidator`). Así el gateway **no puede emitir** tickets, solo verificarlos.
|
||||
- Claims: `sub`, `prefix`, `aud = media-gateway`, `iss = musify-webapi`, `exp` (TTL ~1h).
|
||||
|
||||
**Por qué RS256 (asimétrica)**: separa responsabilidades — emisor (WebApi) y verificador (gateway) no comparten secreto; el gateway, aunque se comprometa, no puede crear permisos.
|
||||
**Por qué RS256 (asimétrica)**: separa responsabilidades — emisor (la API) y verificador (gateway) no comparten secreto; el gateway, aunque se comprometa, no puede crear permisos.
|
||||
|
||||
## El gateway (validación + proxy)
|
||||
|
||||
@@ -44,7 +44,7 @@ Se autoriza **el prefijo de la carpeta** de la pista con un *stream-ticket* de c
|
||||
|
||||
## ¿Un ticket por canción?
|
||||
|
||||
Sí. Cada ticket está acotado a **una** carpeta (una pista). Para reproducir otra, se pide otro ticket. Es barato (la WebApi solo firma un JWT) y es lo más seguro: cada permiso abre solo lo que vas a reproducir.
|
||||
Sí. Cada ticket está acotado a **una** carpeta (una pista). Para reproducir otra, se pide otro ticket. Es barato (la API solo firma un JWT) y es lo más seguro: cada permiso abre solo lo que vas a reproducir.
|
||||
|
||||
- Si algún día molesta (muchísimos cambios de pista), el mismo mecanismo del claim `prefix` permite ampliar el alcance: a nivel de playlist o de toda la biblioteca (`Tracks/ProcessedAudios/`). Trade-off seguridad ↔ comodidad. Por defecto, **por canción**.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user