mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Estructura del monorepo, arquitectura de aplicaciones y paquetes, ciclo de vida de las solicitudes y huella de recursos de SnapOtter."
|
||||
i18n_output_hash: 95af8eadf83e
|
||||
i18n_source_hash: a53946e760b0
|
||||
i18n_source_hash: 50e076925c4b
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: c72b34a809f5
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Arquitectura {#architecture}
|
||||
@@ -52,7 +53,7 @@ Tipos de TypeScript compartidos, constantes (como `APP_VERSION` y definiciones d
|
||||
|
||||
### API (`apps/api`) {#api-apps-api}
|
||||
|
||||
Un servidor Fastify v5 que expone 241 rutas de herramientas en cinco modalidades (imagen, vídeo, audio, PDF, archivo) y que gestiona:
|
||||
Un servidor Fastify v5 que expone 243 rutas de herramientas en cinco modalidades (imagen, vídeo, audio, PDF, archivo) y que gestiona:
|
||||
- Subidas de archivos, gestión del espacio de trabajo temporal y almacenamiento persistente de archivos
|
||||
- Biblioteca de archivos de usuario (tabla `user_files`): una edición guardada se almacena de forma predeterminada como un nuevo archivo independiente, o como una versión enlazada a su padre cuando sobrescribes el original. Registra qué herramientas se aplicaron (`toolChain`) y obtiene una miniatura autogenerada para la página de Archivos
|
||||
- Ejecución de herramientas (dirige cada solicitud de herramienta al motor de imágenes o al puente de IA)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Todas las variables de entorno de SnapOtter con sus valores predeterminados. Configura autenticación, almacenamiento, modelos de IA, analítica y más."
|
||||
i18n_source_hash: 8e9e9ca2840c
|
||||
i18n_source_hash: 25970c776f7c
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: e405b8a429bd
|
||||
i18n_output_hash: 469f3ab3c81c
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Configuración {#configuration}
|
||||
@@ -19,28 +20,51 @@ Toda la configuración se realiza mediante variables de entorno. Cada variable t
|
||||
| `RATE_LIMIT_PER_MIN` | `1000` | Máximo de solicitudes por minuto por IP. Ponlo a 0 para desactivar la limitación de tasa. |
|
||||
| `CORS_ORIGIN` | (vacío) | Orígenes permitidos para CORS separados por comas, o vacío para solo el mismo origen. |
|
||||
| `LOG_LEVEL` | `info` | Verbosidad del registro. Uno de: `fatal`, `error`, `warn`, `info`, `debug`, `trace`. |
|
||||
| `TRUST_PROXY` | `true` | Confía en las cabeceras `X-Forwarded-For` de un proxy inverso. Ponlo a `false` si no está detrás de un proxy. |
|
||||
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Qué pares pueden establecer la IP del cliente mediante `X-Forwarded-For`. El valor predeterminado solo cree a un par de una red privada, así que un proxy inverso en una red de Docker o en una LAN sí es de confianza y la cabecera falsificada de un cliente público no. Pon `true` solo cuando delante haya un proxy bajo tu control en una dirección pública. |
|
||||
|
||||
### Autenticación {#authentication}
|
||||
|
||||
Los dos valores booleanos de abajo solo aceptan `true` y `false`. Cualquier otra cosa, ya sea `1`, `yes` u `on`, no supera la validación y el servidor termina antes de ponerse a escuchar.
|
||||
|
||||
| Variable | Predeterminado | Descripción |
|
||||
|---|---|---|
|
||||
| `AUTH_ENABLED` | `false` | Ponlo a `true` para requerir inicio de sesión. La imagen de Docker usa `true` por defecto. |
|
||||
| `AUTH_ENABLED` | `true` | Exige un inicio de sesión. Ponlo a `false` para funcionar sin ninguna cuenta, lo que concede permisos de administrador a todas las solicitudes, así que resérvalo para una red de confianza. |
|
||||
| `DEFAULT_USERNAME` | `admin` | Nombre de usuario de la cuenta de administrador inicial. Solo se usa en la primera ejecución. |
|
||||
| `DEFAULT_PASSWORD` | `admin` | Contraseña de la cuenta de administrador inicial. Cámbiala tras el primer inicio de sesión. |
|
||||
| `MAX_USERS` | `0` (ilimitado) | Número máximo de cuentas de usuario registradas. Ponlo a 0 para ilimitado. |
|
||||
| `SESSION_DURATION_HOURS` | `168` | Duración de la sesión de inicio de sesión en horas (el valor predeterminado es 7 días). |
|
||||
| `SKIP_MUST_CHANGE_PASSWORD` | - | Ponlo a cualquier valor no vacío para omitir el aviso obligatorio de cambio de contraseña en el primer inicio de sesión |
|
||||
| `SKIP_MUST_CHANGE_PASSWORD` | `false` | Ponlo a `true` para omitir el aviso obligatorio de cambio de contraseña en el primer inicio de sesión. |
|
||||
|
||||
### Almacenamiento {#storage}
|
||||
|
||||
| Variable | Predeterminado | Descripción |
|
||||
|---|---|---|
|
||||
| `STORAGE_MODE` | `local` | `local` o `s3`. S3/MinIO requiere una licencia con la función s3_storage. |
|
||||
| `DATABASE_URL` | `postgres://snapotter:snapotter@postgres:5432/snapotter` | Cadena de conexión de PostgreSQL. |
|
||||
| `REDIS_URL` | `redis://redis:6379` | Cadena de conexión de Redis (usada para las colas de tareas de BullMQ). |
|
||||
| `WORKSPACE_PATH` | `./tmp/workspace` | Directorio para archivos temporales durante el procesamiento. Se limpia automáticamente. |
|
||||
| `FILES_STORAGE_PATH` | `./data/files` | Directorio para archivos de usuario persistentes (imágenes subidas, resultados guardados). |
|
||||
| `STORAGE_MODE` | `local` | `local` o `s3`. S3 y MinIO necesitan una licencia con la función s3_storage, además de las variables `S3_*` de abajo. |
|
||||
| `DATABASE_URL` | `postgres://snapotter:snapotter@localhost:5432/snapotter` | Cadena de conexión de PostgreSQL. La pila de Compose la apunta a su servicio `postgres`; déjala sin establecer (junto con `REDIS_URL`) para obtener el modo embebido. |
|
||||
| `REDIS_URL` | `redis://localhost:6379` | Cadena de conexión de Redis (usada para las colas de tareas de BullMQ). Compose la apunta a su servicio `redis`. |
|
||||
| `WORKSPACE_PATH` | `./tmp/workspace` | Directorio para archivos temporales durante el procesamiento. Se limpia automáticamente. La imagen establece `/tmp/workspace`. |
|
||||
| `FILES_STORAGE_PATH` | `./data/files` | Directorio para archivos de usuario persistentes (imágenes subidas, resultados guardados). La imagen establece `/data/files`. |
|
||||
|
||||
### Almacenamiento de objetos S3 {#s3-object-storage}
|
||||
|
||||
Solo se leen cuando `STORAGE_MODE=s3`. Si falta alguna de las tres obligatorias, el arranque falla indicando el nombre de la variable que omitiste.
|
||||
|
||||
| Variable | Predeterminado | Descripción |
|
||||
|---|---|---|
|
||||
| `S3_BUCKET` | (vacío) | Bucket que contiene las subidas y las salidas. Obligatorio. |
|
||||
| `S3_ACCESS_KEY_ID` | (vacío) | Clave de acceso. Obligatoria. En el contenedor puedes montarla en su lugar, mediante `S3_ACCESS_KEY_ID_FILE`. |
|
||||
| `S3_SECRET_ACCESS_KEY` | (vacío) | Clave secreta. Obligatoria. Misma convención de archivo: `S3_SECRET_ACCESS_KEY_FILE`. |
|
||||
| `S3_REGION` | `us-east-1` | Región del bucket. |
|
||||
| `S3_ENDPOINT` | (vacío) | Endpoint personalizado para MinIO, R2, Backblaze y otros almacenes compatibles con S3. Vacío significa AWS. |
|
||||
| `S3_FORCE_PATH_STYLE` | `false` | Ponlo a `true` para MinIO y para cualquier otro que espere `endpoint/bucket/key` en lugar del direccionamiento por host virtual. |
|
||||
| `S3_PREFIX` | (vacío) | Prefijo de clave, para que un mismo bucket pueda alojar varias instancias. |
|
||||
|
||||
### Cifrado en reposo {#encryption-at-rest}
|
||||
|
||||
| Variable | Predeterminado | Descripción |
|
||||
|---|---|---|
|
||||
| `DATA_ENCRYPTION_KEY` | (vacío) | 64 caracteres hexadecimales (32 bytes). Cifra los ajustes sensibles almacenados en la base de datos. Cualquier cosa que no sean 64 caracteres hexadecimales se rechaza al arrancar. |
|
||||
| `DATA_ENCRYPTION_KEY_PREVIOUS` | (vacío) | La clave que estás dejando atrás en una rotación, con el mismo formato. Establece ambas durante la rotación para que las filas existentes se sigan descifrando, y luego elimina esta. |
|
||||
|
||||
### Modo embebido {#embedded-mode}
|
||||
|
||||
@@ -59,16 +83,15 @@ Nota sobre telemetría: el modo embebido hereda el valor predeterminado de anal
|
||||
|
||||
| Variable | Predeterminado | Descripción |
|
||||
|---|---|---|
|
||||
| `MAX_UPLOAD_SIZE_MB` | `100` | Tamaño máximo de archivo por subida en megabytes. Ponlo a 0 para ilimitado. |
|
||||
| `MAX_BATCH_SIZE` | `100` | Número máximo de archivos en una sola solicitud por lotes. Ponlo a 0 para ilimitado. |
|
||||
| `MAX_UPLOAD_SIZE_MB` | `0` (ilimitado) | Tamaño máximo de archivo por subida en megabytes. Ponlo a 0 para ilimitado. La imagen publicada se distribuye con `0`; una compilación desde el código fuente empieza en 100. |
|
||||
| `MAX_BATCH_SIZE` | `0` (ilimitado) | Número máximo de archivos en una sola solicitud por lotes. Ponlo a 0 para ilimitado. La imagen publicada se distribuye con `0`; una compilación desde el código fuente empieza en 100. |
|
||||
| `CONCURRENT_JOBS` | `0` (automático) | Número de tareas por lotes que se ejecutan en paralelo. Ponlo a 0 para detectarlo automáticamente según los núcleos de CPU disponibles. |
|
||||
| `MAX_MEGAPIXELS` | `0` (ilimitado) | Resolución máxima de imagen permitida en megapíxeles. Ponlo a 0 para ilimitado. |
|
||||
| `MAX_WORKER_THREADS` | `0` (automático) | Máximo de hilos de trabajo para el procesamiento de imágenes. Ponlo a 0 para detectarlo automáticamente según los núcleos de CPU disponibles. |
|
||||
| `PROCESSING_TIMEOUT_S` | `0` (sin límite) | Tiempo máximo de procesamiento por solicitud en segundos. Ponlo a 0 para que no haya tiempo de espera. |
|
||||
| `MAX_PIPELINE_STEPS` | `20` | Número máximo de pasos en una canalización. Ponlo a 0 para que no haya límite. |
|
||||
| `MAX_CANVAS_PIXELS` | `0` (sin límite) | Tamaño máximo del lienzo en píxeles para las imágenes de salida. Ponlo a 0 para que no haya límite. |
|
||||
| `MAX_SVG_SIZE_MB` | `0` (ilimitado) | Tamaño máximo de archivo SVG en megabytes. Ponlo a 0 para ilimitado. |
|
||||
| `MAX_SPLIT_GRID` | `100` | Dimensión máxima de la cuadrícula para la herramienta de división de imágenes. |
|
||||
| `MAX_SVG_SIZE_MB` | `50` | El SVG más grande que se acepta antes de sanearlo, en megabytes. Aquí `0` se comporta de forma distinta que en las filas de alrededor. Elimina por completo el límite de tamaño previo al análisis en lugar de subirlo, así que deja esta variable con un valor. |
|
||||
| `MAX_PDF_PAGES` | `0` (ilimitado) | Número máximo de páginas de PDF para la conversión de PDF a imagen. Ponlo a 0 para ilimitado. |
|
||||
|
||||
### Limpieza {#cleanup}
|
||||
@@ -82,7 +105,7 @@ Nota sobre telemetría: el modo embebido hereda el valor predeterminado de anal
|
||||
|
||||
| Variable | Predeterminado | Descripción |
|
||||
|---|---|---|
|
||||
| `DEFAULT_THEME` | `light` | Tema predeterminado para las sesiones nuevas. `light` o `dark`. |
|
||||
| `DEFAULT_THEME` | `light` | Tema predeterminado para las sesiones nuevas. `light`, `dark` o `system`. |
|
||||
| `DEFAULT_LOCALE` | `en` | Idioma predeterminado de la interfaz. |
|
||||
| `DEFAULT_TOOL_VIEW` | `sidebar` | Diseño de herramienta predeterminado. `sidebar` o `fullscreen`. |
|
||||
|
||||
@@ -124,13 +147,13 @@ services:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_PASSWORD: snapotter # Cambie esto para implementaciones no locales
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Cómo contribuir a SnapOtter. Informes de errores, solicitudes de funciones, pull requests y requisitos del CLA."
|
||||
i18n_source_hash: 528802503035
|
||||
i18n_source_hash: 6c920a5f83e0
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: 27b5c04c8d90
|
||||
i18n_output_hash: 406a2e39b199
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Contribuir {#contributing}
|
||||
@@ -53,7 +54,7 @@ Si contribuyes en nombre de tu empleador y este conserva los derechos de propied
|
||||
|
||||
### Requisitos previos {#prerequisites}
|
||||
|
||||
- Node.js 22+
|
||||
- Node.js 22.22+
|
||||
- pnpm 9+
|
||||
- Python 3.11+ (solo para herramientas de IA)
|
||||
- Docker (opcional, para pruebas de integración completas)
|
||||
@@ -71,7 +72,7 @@ docker compose -f docker-compose.dev.yml up -d
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
|
||||
# Start dev servers (web on :1349, API on :13490)
|
||||
# Start dev servers (web on :1351, API on :13490)
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Esquema de la base de datos PostgreSQL, tablas, migraciones y procedimientos de copia de seguridad de SnapOtter."
|
||||
i18n_source_hash: 50d5d4f220cf
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: 70b4aa5ae152
|
||||
i18n_source_hash: a68264552836
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: 98e6d1aeab16
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Base de datos {#database}
|
||||
@@ -145,6 +146,17 @@ Registro de acciones relevantes para la seguridad.
|
||||
| `details` | jsonb | Datos específicos de la acción |
|
||||
| `createdAt` | timestamp | Momento de la acción |
|
||||
|
||||
### user_preferences {#user-preferences}
|
||||
|
||||
Estado de la interfaz por usuario, indexado por nombre de preferencia. Almacena las herramientas fijadas de la página de inicio, que se escriben a través de `PUT /api/v1/preferences`.
|
||||
|
||||
| Columna | Tipo | Notas |
|
||||
|---|---|---|
|
||||
| `userId` | text | FK a users, con borrado en cascada. Clave primaria junto con `key` |
|
||||
| `key` | text | Nombre de la preferencia. Clave primaria junto con `userId` |
|
||||
| `value` | jsonb | Contenido de la preferencia |
|
||||
| `updatedAt` | timestamp | Última escritura |
|
||||
|
||||
## Migraciones {#migrations}
|
||||
|
||||
Drizzle gestiona las migraciones del esquema. Los archivos de migración están en `apps/api/drizzle/`. Durante el desarrollo:
|
||||
@@ -161,25 +173,33 @@ En producción, las migraciones pendientes se aplican automáticamente al arranc
|
||||
|
||||
La base de datos relacional reside en el volumen `SnapOtter-pgdata` del contenedor de Postgres, no en el volumen `/data` de la aplicación.
|
||||
|
||||
**Opción 1: pg_dump (recomendada)**
|
||||
**Copia de seguridad lógica con validación (recomendado)**
|
||||
|
||||
```bash
|
||||
# Dump the database while the stack is running
|
||||
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql
|
||||
# Dump into PostgreSQL's portable custom archive format
|
||||
docker exec SnapOtter-postgres \
|
||||
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
|
||||
test -s snapotter.dump
|
||||
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
|
||||
|
||||
# Restore into a fresh database
|
||||
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter
|
||||
# Restore into a fresh/disposable target first and fail on the first SQL error
|
||||
docker exec -i SnapOtter-postgres \
|
||||
pg_restore --exit-on-error --clean --if-exists --no-owner \
|
||||
-U snapotter -d snapotter < snapotter.dump
|
||||
```
|
||||
|
||||
**Opción 2: Instantánea del volumen**
|
||||
Este volcado de base de datos no contiene objetos de biblioteca guardados en `/data/files` ni en estado BullMQ duradero en Redis. Realice una copia de seguridad y restaure aquellos con el procedimiento coordinado en [Seguridad y refuerzo](/es/guide/security#backup-and-recovery).
|
||||
|
||||
**Instantánea del volumen frío**
|
||||
|
||||
```bash
|
||||
# Stop the stack, then snapshot the pgdata volume
|
||||
docker compose down
|
||||
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
|
||||
# Stop every service first, then use your storage platform to snapshot the
|
||||
# PostgreSQL, app-data, and Redis volumes as one crash-consistent set.
|
||||
docker compose -f docker/docker-compose.yml stop
|
||||
```
|
||||
|
||||
No copie un directorio de datos de PostgreSQL activo con `tar`. Redacte los nombres de los volúmenes con prefijos por proyecto, de modo que resuelva los ID de los volúmenes montados desde `docker inspect` o su plataforma de almacenamiento en lugar de asumir la etiqueta literal `SnapOtter-pgdata`.
|
||||
|
||||
### Migrar desde 1.x (SQLite) {#migrating-from-1-x-sqlite}
|
||||
|
||||
Actualizar desde SnapOtter 1.x tiene su propia guía: consulta [Actualizar de 1.x a 2.0](./upgrading). En resumen, reutiliza tu volumen `/data` existente y 2.0 detecta e importa automáticamente `/data/snapotter.db` en el primer arranque (o define `SQLITE_MIGRATE_PATH` para apuntar a él explícitamente). Haz primero una copia de seguridad de todo el volumen `/data`, no solo de `snapotter.db`: 1.x usa el modo WAL de SQLite, por lo que un contenedor detenido suele dejar la mayor parte de sus datos en `snapotter.db-wal` junto a un `snapotter.db` casi vacío.
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Despliega SnapOtter en producción con Docker. Requisitos de hardware, configuración de GPU y configuraciones de proxy inverso para Nginx, Traefik y Cloudflare."
|
||||
i18n_output_hash: 8d748bf9af34
|
||||
i18n_source_hash: 98172965118b
|
||||
i18n_source_hash: 2a722f86da75
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: 5c1aeeb99290
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Despliegue {#deployment}
|
||||
@@ -47,7 +48,7 @@ services:
|
||||
# - MAX_USERS=0 # Max user accounts
|
||||
|
||||
# --- Networking ---
|
||||
# - TRUST_PROXY=true # Trust X-Forwarded-For headers (set false if not behind a proxy)
|
||||
# - TRUST_PROXY=loopback,linklocal,uniquelocal # Which peers may set the client IP via X-Forwarded-For (default shown)
|
||||
|
||||
# --- Bind mount permissions ---
|
||||
# - PUID=1000 # Match your host user's UID (run: id -u)
|
||||
@@ -82,7 +83,7 @@ services:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
@@ -170,13 +171,13 @@ services:
|
||||
container_name: SnapOtter-postgres
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_PASSWORD: snapotter # Cambie esto para implementaciones no locales
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
@@ -207,13 +208,17 @@ volumes:
|
||||
docker compose -f docker-compose-gpu.yml up -d
|
||||
```
|
||||
|
||||
Comprueba la detección de CUDA en los registros:
|
||||
### Verificar la aceleración de la GPU {#verify-gpu-acceleration}
|
||||
|
||||
Verifique la detección de CUDA en los registros:
|
||||
|
||||
```bash
|
||||
docker logs SnapOtter 2>&1 | head -20
|
||||
# Look for: [gpu] CUDA available via torch
|
||||
```
|
||||
|
||||
Si las herramientas de IA se ejecutan en la CPU aunque `--gpus all` y NVIDIA Container Toolkit estén configurados correctamente, reinstale el paquete afectado (por ejemplo, Eliminación de fondo) desde **Configuración → Funciones de IA**. El instalador restaura la compilación de GPU de ONNX Runtime, que de otro modo una compilación de solo CPU extraída por otro paquete (como la transcripción) puede ocultar en el entorno de IA compartido. Si la reinstalación desde la interfaz de usuario no restaura la GPU en una imagen anterior, consulte la reparación manual en [problema n.° 490] (https://github.com/snapotter-hq/SnapOtter/issues/490).
|
||||
|
||||
## Requisitos de hardware {#hardware-requirements}
|
||||
|
||||
Estos números provienen de pruebas de rendimiento en una variedad de sistemas, desde una estación de trabajo amd64 moderna con una NVIDIA RTX 4070 hasta una Raspberry Pi, ejecutando todo el catálogo de herramientas en cada uno y ajustando los límites de recursos de Docker para encontrar el mínimo real.
|
||||
@@ -436,11 +441,11 @@ El error de arranque nombra el UID exacto que hay que usar, así que la vía má
|
||||
| `AUTH_ENABLED` | `true` | Habilita/deshabilita el requisito de inicio de sesión |
|
||||
| `DEFAULT_USERNAME` | `admin` | Nombre de usuario inicial del administrador |
|
||||
| `DEFAULT_PASSWORD` | `admin` | Contraseña inicial del administrador (cambio forzado en el primer inicio de sesión) |
|
||||
| `MAX_UPLOAD_SIZE_MB` | `100` | Límite de subida por archivo |
|
||||
| `MAX_BATCH_SIZE` | `100` | Máximo de archivos por solicitud de lote |
|
||||
| `MAX_UPLOAD_SIZE_MB` | `0` (ilimitado) | Límite de subida por archivo en MB. La imagen viene con `0`; una compilación desde el código fuente arranca en 100 |
|
||||
| `MAX_BATCH_SIZE` | `0` (ilimitado) | Máximo de archivos por solicitud de lote. La imagen viene con `0`; una compilación desde el código fuente arranca en 100 |
|
||||
| `RATE_LIMIT_PER_MIN` | `1000` | Solicitudes de API por minuto por IP (configura 0 para deshabilitar) |
|
||||
| `MAX_USERS` | `0` (ilimitado) | Máximo de cuentas de usuario |
|
||||
| `TRUST_PROXY` | `true` | Confiar en las cabeceras X-Forwarded-For del proxy inverso |
|
||||
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Qué pares pueden establecer la IP del cliente mediante `X-Forwarded-For`. Solo redes privadas de forma predeterminada |
|
||||
| `PUID` | `999` | Ejecutar como este UID (para permisos de montajes de enlace) |
|
||||
| `PGID` | `999` | Ejecutar como este GID (para permisos de montajes de enlace) |
|
||||
| `LOG_LEVEL` | `info` | Verbosidad del registro: fatal, error, warn, info, debug, trace |
|
||||
@@ -483,7 +488,13 @@ curl http://localhost:1349/api/v1/health
|
||||
|
||||
## Proxy inverso {#reverse-proxy}
|
||||
|
||||
SnapOtter establece `TRUST_PROXY=true` por defecto para que la limitación de tasa y el registro usen la IP real del cliente de las cabeceras `X-Forwarded-For`.
|
||||
`TRUST_PROXY` vale `loopback,linklocal,uniquelocal` de forma predeterminada, así que SnapOtter solo cree la cabecera `X-Forwarded-For` de un par que esté en una red privada. Un proxy inverso en el mismo host, en una red de Docker o en tu LAN es de confianza desde el primer momento, de modo que la limitación de tasa, el limitador de fuerza bruta del inicio de sesión, el registro de auditoría y la lista de IP permitidas de la edición enterprise ven la IP real del cliente sin configurar nada.
|
||||
|
||||
Pon `TRUST_PROXY=true` solo cuando el proxy que tienes delante llegue a SnapOtter desde una dirección **pública**, por ejemplo un balanceador de carga en la nube situado en otra red. En una instancia expuesta directamente, ese valor deja `request.ip` en manos del atacante, porque quien va rotando la cabecera consigue un contador de límite de tasa nuevo en cada solicitud.
|
||||
|
||||
Dos cosas conviene saber antes de ponerse a medir IP de cliente. Docker Desktop en macOS y Windows sirve un puerto publicado a través de un proxy en espacio de usuario que reescribe todas las direcciones de origen a la puerta de enlace de la VM `192.168.65.1`, así que ahí ningún valor de `TRUST_PROXY` recupera al cliente real; despliega en Linux cualquier cosa expuesta a internet. Y en cualquier plataforma, llegar a un puerto publicado por `localhost` se observa como la puerta de enlace del puente y no como tu cliente, de manera que una prueba en localhost no dice nada sobre cómo se atribuye un cliente real. La tabla completa de valores de `TRUST_PROXY` y la advertencia sobre Docker Desktop están en [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md#client-ip-resolution-trust_proxy).
|
||||
|
||||
Dos cosas importan para cada proxy a continuación: permitir cuerpos de solicitud (cargas) de gran tamaño y no almacenar en búfer las respuestas. Un proxy de almacenamiento en búfer de respuesta interrumpe el progreso de SSE y, de manera más visible, hace que la descarga de un archivo grande "comience pero nunca termine", porque el proxy retiene el archivo completo antes de pasarlo. SnapOtter envía `X-Accel-Buffering: no` en las descargas para que nginx las transmita incluso si el almacenamiento en búfer se deja activado en otro lugar, pero los servidores proxy distintos de nginx necesitan que el búfer de respuesta esté deshabilitado explícitamente (se muestra en cada configuración a continuación). Si una descarga se detiene parcialmente, lo primero que debe verificar es un proxy de almacenamiento en búfer al frente.
|
||||
|
||||
### Nginx {#nginx}
|
||||
|
||||
@@ -505,7 +516,7 @@ server {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# SSE support (batch progress, feature install progress)
|
||||
# Transmita respuestas en lugar de almacenar en búfer: necesario para el progreso de SSE (lotes, IA, instalaciones de funciones) y para descargas de archivos grandes.
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
@@ -549,7 +560,7 @@ images.example.com {
|
||||
}
|
||||
```
|
||||
|
||||
`flush_interval -1` deshabilita el almacenamiento en búfer de la respuesta, que es necesario para los eventos de progreso SSE (procesamiento por lotes, herramientas de IA, instalaciones de funciones). Los tiempos de espera extendidos permiten que las subidas de archivos grandes se completen sin que Caddy cierre la conexión antes de tiempo.
|
||||
`flush_interval -1` deshabilita el almacenamiento en búfer de respuesta, que es necesario para los eventos de progreso de SSE (procesamiento por lotes, herramientas de inteligencia artificial, instalaciones de funciones) y para que las descargas de archivos grandes se transmitan en lugar de detenerse. Los tiempos de espera extendidos permiten que se completen las cargas de archivos grandes sin que Caddy cierre la conexión antes de tiempo.
|
||||
|
||||
### Túneles de Cloudflare {#cloudflare-tunnels}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Configuración del entorno de desarrollo local, comandos, convenciones de código y cómo añadir una nueva herramienta a SnapOtter."
|
||||
i18n_source_hash: cb03724d2829
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: 572206416dc1
|
||||
i18n_source_hash: 56acc1bf9a9b
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: 334c91e72b73
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Guía del desarrollador {#developer-guide}
|
||||
@@ -11,12 +12,12 @@ Cómo configurar un entorno de desarrollo local y contribuir con código a SnapO
|
||||
|
||||
## Requisitos previos {#prerequisites}
|
||||
|
||||
- [Node.js](https://nodejs.org/) 22+
|
||||
- [Node.js](https://nodejs.org/) 22.22+
|
||||
- [pnpm](https://pnpm.io/) 9+ (`corepack enable && corepack prepare pnpm@latest --activate`)
|
||||
- [Docker](https://www.docker.com/) (requerido para Postgres + Redis locales, construcciones de contenedores y funciones de IA)
|
||||
- Git
|
||||
|
||||
Python 3.10+ solo es necesario si trabajas en el sidecar de IA/ML (eliminación de fondo, escalado, OCR).
|
||||
Python 3.11+ solo es necesario si trabajas en el sidecar de IA/ML (eliminación de fondo, escalado, OCR).
|
||||
|
||||
## Configuración {#setup}
|
||||
|
||||
@@ -32,10 +33,10 @@ Esto inicia dos servidores de desarrollo:
|
||||
|
||||
| Servicio | URL | Notas |
|
||||
|----------|--------------------------|------------------------------------|
|
||||
| Frontend | http://localhost:1349 | Servidor de desarrollo Vite, hace proxy de /api |
|
||||
| Frontend | http://localhost:1351 | Servidor de desarrollo Vite, hace proxy de /api |
|
||||
| Backend | http://localhost:13490 | API de Fastify (accedida vía proxy) |
|
||||
|
||||
Abre http://localhost:1349 en tu navegador. Inicia sesión con `admin` / `admin`. Se te pedirá que cambies la contraseña en el primer inicio de sesión.
|
||||
Abre http://localhost:1351 en tu navegador. Inicia sesión con `admin` / `admin`. Se te pedirá que cambies la contraseña en el primer inicio de sesión.
|
||||
|
||||
## Estructura del proyecto {#project-structure}
|
||||
|
||||
@@ -220,6 +221,17 @@ Usa las cache mounts de BuildKit para reconstrucciones más rápidas:
|
||||
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile -t snapotter:latest .
|
||||
```
|
||||
|
||||
## Dominios de versión de lanzamiento {#release-version-domains}
|
||||
|
||||
SnapOtter tiene intencionalmente tres dominios de versión. No copie un dominio en otro durante un lanzamiento:
|
||||
|
||||
- La versión de lanzamiento de la aplicación cubre el manifiesto raíz, todos los paquetes de espacios de trabajo privados y `APP_VERSION`. Semantic-release proporciona este valor y `pnpm version:sync <version>` actualiza cada espacio de trabajo antes del lanzamiento de una aplicación.
|
||||
- OpenAPI `info.version` es el contrato principal público estable API. Todas las especificaciones localizadas permanecen en `<major>.0.0` para versiones de aplicaciones compatibles y cambian solo cuando el contrato API pasa a una nueva versión principal.
|
||||
- `docker/feature-manifest.json` mantiene a `imageVersion: 2.0.0` como la época de almacenamiento de paquetes de funciones heredadas e inmutables. Esas rutas de archivo v2 no son versiones de paquetes de aplicaciones. Accurate OCR utiliza el formato de tiempo de ejecución v3 y registra el origen de la versión de la aplicación por separado.
|
||||
|
||||
`tests/unit/infra/release-version-policy.test.ts` impone estos límites. Una nueva versión de dominio o migración debe actualizar ese contrato y el diseño de migración de artefacto relevante juntos.
|
||||
Los valores independientes API y del paquete heredado se encuentran en `config/release-version-policy.json`; La sincronización de la versión de la aplicación nunca debe reescribir ese archivo de política implícitamente.
|
||||
|
||||
## Variables de entorno {#environment-variables}
|
||||
|
||||
Consulta la [Guía de configuración](/es/guide/configuration) para la lista completa. Las clave para el desarrollo:
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Etiquetas de la imagen Docker de SnapOtter, benchmarks de GPU, fijado de versiones y compatibilidad multiplataforma para AMD64 y ARM64."
|
||||
i18n_output_hash: d1aeb361b929
|
||||
i18n_source_hash: fda322e78b4b
|
||||
i18n_source_hash: 566e20ca07fc
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: 3be2193d3085
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Imagen Docker {#docker-image}
|
||||
@@ -93,13 +94,13 @@ services:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_PASSWORD: snapotter # Cambie esto para implementaciones no locales
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
@@ -140,9 +141,9 @@ Para la aceleración NVIDIA CUDA mediante Docker Compose, añade la sección dep
|
||||
| Etiqueta | Descripción |
|
||||
|-----|------------|
|
||||
| `latest` | Última versión |
|
||||
| `1.11.0` | Versión exacta |
|
||||
| `1.11` | Último parche en 1.11.x |
|
||||
| `1` | Última versión menor en 1.x |
|
||||
| `2.1.0` | Versión exacta |
|
||||
| `2.1` | Último parche en 2.1.x |
|
||||
| `2` | Última versión menor en 2.x |
|
||||
|
||||
## Plataformas {#platforms}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Instala SnapOtter con Docker en un solo comando. Incluye la configuración de Docker Compose, la compilación desde el código fuente y una descripción completa de las funciones."
|
||||
i18n_output_hash: d513d36ca8ba
|
||||
i18n_source_hash: 68bf7f60b68d
|
||||
i18n_provenance: human
|
||||
i18n_source_hash: 8040133a6982
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: 5d9ea3a3420e
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Primeros pasos {#getting-started}
|
||||
@@ -17,7 +18,7 @@ Explora la interfaz completa en [demo.snapotter.com](https://demo.snapotter.com)
|
||||
docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data snapotter/snapotter:latest
|
||||
```
|
||||
|
||||
Este único contenedor ejecuta todo lo que necesita: sin `DATABASE_URL` definido, inicia su propio PostgreSQL y Redis en la interfaz de loopback (modo embebido) y mantiene todos los datos en el volumen `SnapOtter-data`. Es la forma más rápida de probar SnapOtter o de autoalojarlo en un homelab. Para producción, ejecuta la pila de [Docker Compose](#docker-compose) que aparece abajo, que mantiene PostgreSQL y Redis en sus propios contenedores. El modo embebido se ejecuta como root (el valor por defecto) y se desactiva automáticamente en cuanto defines `DATABASE_URL`.
|
||||
Este único contenedor ejecuta todo lo que necesita: sin `DATABASE_URL` configurado, inicia su propio PostgreSQL y Redis en la interfaz loopback (modo integrado) y mantiene todos los datos en el volumen `SnapOtter-data`. Es la forma más rápida de probar SnapOtter o autohospedarse en un laboratorio doméstico. Para producción, utilice la [pila canónica de Docker Compose](#docker-compose), que mantiene PostgreSQL y Redis en sus propios contenedores. El modo integrado se ejecuta como root (el valor predeterminado) y se apaga automáticamente tan pronto como configura `DATABASE_URL`.
|
||||
|
||||
¿Vas a instalar en una Raspberry Pi, un portátil viejo o un VPS pequeño? Consulta [Configuraciones con recursos limitados](/es/guide/low-resource) para una guía paso a paso ajustada y para saber qué esperar de un hardware limitado.
|
||||
|
||||
@@ -40,7 +41,7 @@ Agregue `--gpus all` para NVIDIA eliminación de fondo, ampliación, mejora faci
|
||||
docker run -d --name SnapOtter -p 1349:1349 --gpus all -v SnapOtter-data:/data snapotter/snapotter:latest
|
||||
```
|
||||
|
||||
Requiere el [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Recurre a la CPU automáticamente cuando CUDA no está disponible. La aceleración por iGPU de Intel/AMD mediante VA-API, Quick Sync u OpenCL no es compatible con la inferencia de IA por ahora. Consulta [Etiquetas de Docker](/es/guide/docker-tags) para las pruebas de rendimiento.
|
||||
Requiere el [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html). Vuelve a la CPU automáticamente cuando CUDA no está disponible. La aceleración Intel/AMD iGPU a través de VA-API, Quick Sync u OpenCL no es compatible con la inferencia de IA en la actualidad. Consulte [Etiquetas Docker](/es/guide/docker-tags) para conocer los puntos de referencia. Si las herramientas de IA se ejecutan en la CPU a pesar de `--gpus all`, consulte [Verificar la aceleración de la GPU](/es/guide/deployment#verify-gpu-acceleration).
|
||||
:::
|
||||
|
||||
::: details También en GHCR
|
||||
@@ -51,67 +52,33 @@ docker run -d --name SnapOtter -p 1349:1349 -v SnapOtter-data:/data ghcr.io/snap
|
||||
Ambos registros publican la misma imagen en cada versión.
|
||||
:::
|
||||
|
||||
## Docker Compose {#docker-compose}
|
||||
## Docker componer {#docker-compose}
|
||||
|
||||
```yaml
|
||||
services:
|
||||
SnapOtter:
|
||||
image: snapotter/snapotter:latest # or ghcr.io/snapotter-hq/snapotter:latest
|
||||
ports:
|
||||
- "1349:1349"
|
||||
volumes:
|
||||
- SnapOtter-data:/data
|
||||
environment:
|
||||
- AUTH_ENABLED=true
|
||||
- DEFAULT_USERNAME=admin
|
||||
- DEFAULT_PASSWORD=admin
|
||||
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
|
||||
- REDIS_URL=redis://redis:6379
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
Utilice el archivo de producción mantenido y probado con cada versión en lugar de copiar un ejemplo de Compose abreviado de esta página:
|
||||
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
```bash
|
||||
install -d -m 700 snapotter && cd snapotter
|
||||
curl --proto '=https' --tlsv1.2 -fsSLo docker-compose.yml \
|
||||
https://raw.githubusercontent.com/snapotter-hq/SnapOtter/v2.1.0/docker/docker-compose.yml
|
||||
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- SnapOtter-redisdata:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
# Keep generated service credentials out of shell history and world-readable files.
|
||||
umask 077
|
||||
POSTGRES_PASSWORD="$(openssl rand -hex 32)"
|
||||
REDIS_PASSWORD="$(openssl rand -hex 32)"
|
||||
printf 'POSTGRES_PASSWORD=%s\nREDIS_PASSWORD=%s\n' \
|
||||
"$POSTGRES_PASSWORD" "$REDIS_PASSWORD" > .env
|
||||
|
||||
volumes:
|
||||
SnapOtter-data:
|
||||
SnapOtter-pgdata:
|
||||
SnapOtter-redisdata:
|
||||
docker compose -f docker-compose.yml pull
|
||||
docker compose -f docker-compose.yml up -d --no-build
|
||||
```
|
||||
|
||||
Consulta [Configuración](/es/guide/configuration) para todas las variables de entorno.
|
||||
El canónico [`docker/docker-compose.yml`](https://github.com/snapotter-hq/SnapOtter/blob/v2.1.0/docker/docker-compose.yml) incluye los cuatro volúmenes de tiempo de ejecución, comprobaciones de estado, límites de recursos, configuración duradera de Redis, imágenes de caché/base de datos fijadas y el refuerzo del contenedor actual. Cambie la contraseña de administrador predeterminada inmediatamente después del primer inicio de sesión. Para una implementación reproducible, fije la imagen de la aplicación SnapOtter a la etiqueta de versión o resumen que verificó en lugar de seguir `latest`.
|
||||
|
||||
Consulte [Configuración](/es/guide/configuration) para conocer todas las variables de entorno y [Seguridad y refuerzo](/es/guide/security) para conocer secretos, políticas de red y orientación sobre copias de seguridad.
|
||||
|
||||
## Compilar desde el código fuente {#build-from-source}
|
||||
|
||||
**Requisitos previos:** Node.js 22+, pnpm 9+, Docker (para Postgres + Redis), Python 3.10+ (para funciones de IA), Git.
|
||||
**Requisitos previos:** Node.js 22.22+, pnpm 9+, Docker (para Postgres + Redis), Python 3.11+ (para funciones de IA), Git.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/snapotter-hq/SnapOtter.git
|
||||
@@ -121,7 +88,7 @@ pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
- Frontend: [http://localhost:1349](http://localhost:1349)
|
||||
- Frontend: [http://localhost:1351](http://localhost:1351)
|
||||
- Backend: [http://localhost:13490](http://localhost:13490)
|
||||
|
||||
## Qué puedes hacer {#what-you-can-do}
|
||||
@@ -130,11 +97,11 @@ pnpm dev
|
||||
|
||||
| Modalidad | Recuento | Herramientas de ejemplo |
|
||||
|----------|-------|---------------|
|
||||
| **Imagen** | 105 | Redimensionar, Recortar, Comprimir, Convertir, Eliminar fondo, Escalar, OCR, Marca de agua, Collage, Colorizar, Herramientas GIF, ajustes de formato predefinidos |
|
||||
| **Imagen** | 107 | Redimensionar, Recortar, Comprimir, Convertir, Eliminar fondo, Escalar, OCR, Marca de agua, Collage, Colorizar, Herramientas GIF, ajustes de formato predefinidos |
|
||||
| **Vídeo** | 57 | Recortar, Recortar marco, Comprimir, Convertir, Combinar, Extraer audio, Subtítulos automáticos, Vídeo a GIF, Redimensionar, Estabilizar, ajustes de formato predefinidos |
|
||||
| **Audio** | 27 | Recortar, Combinar, Convertir, Normalizar, Reducción de ruido, Transcribir, Cambio de tono, Fundido, Creador de tonos de llamada, ajustes de formato predefinidos |
|
||||
| **PDF / Documento** | 42 | Combinar, Dividir, Comprimir, OCR, Marca de agua, Redactar, Word a PDF, Excel a PDF, Rotar, Proteger, Reparar |
|
||||
| **Archivos** | 10 | CSV a JSON, JSON a XML, Combinar CSVs, Dividir CSV, Crear ZIP, Extraer ZIP, Creador de gráficos, YAML/JSON |
|
||||
| **PDF / Documento** | 29 | Combinar, Dividir, Comprimir, OCR, Marca de agua, Redactar, Word a PDF, Excel a PDF, Rotar, Proteger, Reparar |
|
||||
| **Archivos** | 23 | CSV a JSON, JSON a XML, Combinar CSVs, Dividir CSV, Crear ZIP, Extraer ZIP, Creador de gráficos, YAML/JSON |
|
||||
|
||||
### Canalizaciones {#pipelines}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
i18n_source_hash: f5de74aee1b9
|
||||
i18n_source_hash: 521c03a6416c
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: c2d85458029e
|
||||
i18n_output_hash: af247a97f6fd
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
# Configuraciones con recursos limitados {#low-resource-setups}
|
||||
|
||||
@@ -59,7 +60,7 @@ services:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=snapotter
|
||||
- POSTGRES_PASSWORD=snapotter
|
||||
- POSTGRES_PASSWORD=snapotter # Cambie esto para implementaciones no locales
|
||||
- POSTGRES_DB=snapotter
|
||||
volumes:
|
||||
- ./postgres-data:/var/lib/postgresql/data
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Configura el aprovisionamiento SCIM 2.0 para sincronizar usuarios y grupos desde tu proveedor de identidad hacia SnapOtter. Cubre Okta, Azure AD / Entra ID e integraciones personalizadas."
|
||||
i18n_source_hash: bbd50119ec12
|
||||
i18n_source_hash: 06ee702b386e
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: 3058636bdbc1
|
||||
i18n_output_hash: db9c6e0b36c9
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Aprovisionamiento SCIM {#scim-provisioning}
|
||||
@@ -17,7 +18,7 @@ El aprovisionamiento SCIM requiere una licencia **enterprise** con la función `
|
||||
|
||||
- Una instancia de SnapOtter en ejecución accesible en una URL pública
|
||||
- Una clave de licencia enterprise con la función `scim`
|
||||
- Acceso de administrador a SnapOtter (se requiere el permiso `users:manage` para generar o revocar un token SCIM)
|
||||
- Una cuenta SnapOtter `admin` integrada con su conjunto completo de permisos efectivos. Una función personalizada delegada o una clave API de administrador a la que le falta algún permiso de administrador no pueden generar ni revocar el token SCIM global.
|
||||
- Acceso de administrador a la configuración de aprovisionamiento de tu proveedor de identidad
|
||||
|
||||
## Inicio rápido {#quick-start}
|
||||
@@ -34,7 +35,7 @@ La respuesta contiene el token. Guárdalo de inmediato; no se puede recuperar de
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "a1b2c3d4e5f6...",
|
||||
"token": "so_scim_v2_a1b2c3d4e5f6...",
|
||||
"message": "Save this token - it cannot be retrieved again"
|
||||
}
|
||||
```
|
||||
@@ -49,15 +50,19 @@ Los endpoints SCIM usan un token Bearer dedicado, independiente de las sesiones
|
||||
|
||||
### Generar un token {#generating-a-token}
|
||||
|
||||
`POST /api/v1/enterprise/scim/token` genera un nuevo token SCIM. Este endpoint requiere una sesión válida con el permiso `users:manage`.
|
||||
`POST /api/v1/enterprise/scim/token` genera un nuevo token SCIM. Debido a que el token puede aprovisionar y mutar usuarios en la instancia, este punto final requiere el rol `admin` integrado con el conjunto completo de permisos de administrador efectivo. Mantener a `users:manage` en una función personalizada no es suficiente.
|
||||
|
||||
El token se devuelve en texto plano exactamente una vez. SnapOtter almacena solo un hash scrypt. Si pierdes el token, revócalo y genera uno nuevo.
|
||||
|
||||
Solo hay un token SCIM activo a la vez. Generar un token nuevo reemplaza al anterior.
|
||||
|
||||
::: warning Reemisión de token después de la actualización
|
||||
Los tokens SCIM heredados y no versionados se rechazan. Después de actualizar a una versión que emite tokens `so_scim_v2_...`, genere un token nuevo y actualice su proveedor de identidad antes de reanudar el aprovisionamiento.
|
||||
:::
|
||||
|
||||
### Revocar un token {#revoking-a-token}
|
||||
|
||||
`DELETE /api/v1/enterprise/scim/token` revoca el token SCIM actual. Este endpoint también requiere `users:manage`.
|
||||
`DELETE /api/v1/enterprise/scim/token` revoca el token SCIM actual. Tiene los mismos requisitos de administración integrados que la generación de tokens.
|
||||
|
||||
### Limitación de tasa {#rate-limiting}
|
||||
|
||||
@@ -279,7 +284,7 @@ La solicitud SCIM no incluía una cabecera `Authorization: Bearer <token>`. Comp
|
||||
|
||||
### 401 "Invalid token" {#_401-invalid-token}
|
||||
|
||||
El token no coincide con el hash almacenado. Esto ocurre si el token fue revocado y regenerado. Actualiza el token en la configuración de aprovisionamiento de tu IdP.
|
||||
El token tiene un formato incorrecto, utiliza el formato no versionado retirado o no coincide con el hash almacenado. Genere un token `so_scim_v2_...` actual y actualícelo en la configuración de aprovisionamiento de su IdP.
|
||||
|
||||
### 401 "SCIM not configured" {#_401-scim-not-configured}
|
||||
|
||||
|
||||
+89
-161
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Guía de fortalecimiento de seguridad para SnapOtter. Seguridad de contenedores, aislamiento de red, secretos de Docker, despliegue en Kubernetes y artefactos de cumplimiento."
|
||||
i18n_source_hash: 986f7658430c
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: ab6055b24928
|
||||
i18n_source_hash: 9ff337fa0417
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: 1cc16fd75817
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Seguridad y fortalecimiento {#security-hardening}
|
||||
@@ -11,133 +12,42 @@ SnapOtter procesa los archivos íntegramente en tu infraestructura. Envía por d
|
||||
|
||||
El contenedor se ejecuta como un usuario dedicado sin root (`snapotter`) con todas las capacidades de Linux eliminadas excepto el conjunto mínimo requerido. Para la política completa de divulgación de vulnerabilidades y la arquitectura de seguridad, consulta [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) en GitHub.
|
||||
|
||||
## Fortalecimiento del contenedor {#container-hardening}
|
||||
## Endurecimiento del contenedor {#container-hardening}
|
||||
|
||||
El [docker-compose.yml por defecto](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) incluye fortalecimiento de seguridad para producción. Aquí tienes un desglose de cada opción y por qué importa:
|
||||
Los archivos canónicos de composición [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) y [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml) son la fuente de la verdad. No copie un ejemplo abreviado en producción; implemente el archivo desde la etiqueta de lanzamiento que verificó.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
SnapOtter:
|
||||
image: snapotter/snapotter:latest
|
||||
ports:
|
||||
# Bind to localhost only for internet-facing deployments:
|
||||
- "127.0.0.1:1349:1349"
|
||||
volumes:
|
||||
- SnapOtter-data:/data
|
||||
- SnapOtter-workspace:/tmp/workspace
|
||||
environment:
|
||||
- AUTH_ENABLED=true
|
||||
- DEFAULT_PASSWORD=change-me-immediately
|
||||
- RATE_LIMIT_PER_MIN=1000
|
||||
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
|
||||
- REDIS_URL=redis://redis:6379
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
Ambas pilas aplican los siguientes controles:
|
||||
|
||||
# --- Resource limits ---
|
||||
mem_limit: 6g # Prevents runaway memory from crashing the host
|
||||
memswap_limit: 6g # No swap - fail fast instead of degrading the host
|
||||
cpus: 4 # Cap CPU usage to 4 cores
|
||||
pids_limit: 512 # Prevents fork bombs
|
||||
- Los límites de memoria, intercambio, CPU y PID contienen procesamiento nativo fuera de control.
|
||||
- Cada servicio elimina todas las capacidades de Linux. La aplicación vuelve a agregar solo `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` para la propiedad del volumen, la caída de identidad unidireccional `gosu` y el reenvío elegante de señales. PostgreSQL y Redis reciben solo el subconjunto que necesitan sus puntos de entrada oficiales.
|
||||
- `security_opt: [no-new-privileges:true]` evita que los procesos en los contenedores de aplicaciones, PostgreSQL y Redis obtengan privilegios adicionales. Esto sigue siendo compatible con `gosu`: el punto de entrada comienza como raíz, prepara los volúmenes y solo llega al usuario dedicado `snapotter`.
|
||||
- Las entradas de imágenes de PostgreSQL y Redis están fijadas mediante resumen. La aplicación también debe fijarse en una etiqueta de lanzamiento verificada o en un resumen en lugar de `latest`.
|
||||
- Las comprobaciones de estado, la rotación de registros JSON limitada, el Redis AOF duradero y la política de reinicio se definen de forma centralizada en los archivos canónicos.
|
||||
|
||||
# --- Capability restrictions ---
|
||||
cap_drop:
|
||||
- ALL # Drop ALL Linux capabilities first
|
||||
cap_add:
|
||||
- CHOWN # Needed for volume permission setup
|
||||
- SETUID # Needed for gosu privilege drop (root -> snapotter)
|
||||
- SETGID # Needed for gosu privilege drop
|
||||
- DAC_OVERRIDE # Needed for volume permission setup
|
||||
- FOWNER # Needed for volume permission setup
|
||||
Para una implementación orientada a Internet, vincule el puerto 1349 al bucle invertido y finalice TLS en un proxy inverso mantenido. Genere credenciales únicas de PostgreSQL y Redis, almacene secretos en archivos protegidos o en un administrador de secretos y cambie la contraseña inicial del administrador de inmediato.
|
||||
|
||||
# --- Logging ---
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "50m" # Rotate logs at 50 MB
|
||||
max-file: "5" # Keep 5 rotated log files
|
||||
### Por qué `read_only` no está configurado como {#why-read-only-is-not-set}
|
||||
|
||||
# --- Health check ---
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "--max-time", "5", "http://localhost:1349/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 60s
|
||||
retries: 3
|
||||
|
||||
shm_size: "2gb" # Required for Python ML shared memory
|
||||
restart: unless-stopped
|
||||
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 15s
|
||||
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- SnapOtter-redisdata:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
SnapOtter-data:
|
||||
SnapOtter-workspace:
|
||||
SnapOtter-pgdata:
|
||||
SnapOtter-redisdata:
|
||||
```
|
||||
|
||||
### Por qué no se establece `no-new-privileges` {#why-no-new-privileges-is-not-set}
|
||||
|
||||
`security_opt: [no-new-privileges:true]` se omite intencionadamente. El punto de entrada arranca como root para corregir la propiedad de los volúmenes, luego cae al usuario `snapotter` mediante [gosu](https://github.com/tianon/gosu), que requiere setuid. Una vez que se completa la reducción de privilegios, el proceso se ejecuta como `snapotter` con todas las capacidades excepto las cinco enumeradas arriba eliminadas.
|
||||
|
||||
Si usas Kubernetes o el indicador `--user` de Docker para ejecutar directamente sin root (evitando gosu), es seguro habilitar `no-new-privileges`.
|
||||
|
||||
### Por qué no se establece `read_only` {#why-read-only-is-not-set}
|
||||
|
||||
`read_only: true` no se establece porque la reasignación de PUID/PGID escribe en `/etc/passwd` y `/etc/group` al arrancar. Si usas el indicador `--user` de Docker o `runAsUser` de Kubernetes en lugar de PUID/PGID, puedes habilitar de forma segura un sistema de archivos raíz de solo lectura.
|
||||
`read_only: true` no está configurado porque la reasignación de PUID/PGID escribe en `/etc/passwd` y `/etc/group` al inicio. Si utiliza el indicador `--user` de Docker o Kubernetes `runAsUser` en lugar de PUID/PGID, puede habilitar de forma segura un sistema de archivos raíz de solo lectura.
|
||||
|
||||
## Aislamiento de red {#network-isolation}
|
||||
|
||||
Durante el funcionamiento normal, el contenedor realiza **cero conexiones de red salientes**. Todo el procesamiento de archivos ocurre localmente usando bibliotecas empaquetadas.
|
||||
El procesamiento de archivos es local, pero una instalación predeterminada **no es un sistema libre de salida**. Los análisis anónimos de productos utilizan PostHog y los informes de fallos utilizan Sentry cuando la telemetría está habilitada. Configure `SNAPOTTER_TELEMETRY=0` (o deshabilite los análisis en Configuración > Sistema > Privacidad) para desactivar ambos. SnapOtter nunca incluye archivos cargados, nombres de archivos, resultados de OCR, texto de documentos u otros contenidos de archivos en esos eventos.
|
||||
|
||||
```
|
||||
Browser --> Reverse Proxy (TLS) --> SnapOtter container --> (nothing)
|
||||
```
|
||||
Otro tráfico saliente se basa en funciones: descargas de instalación de modelos/paquetes de IA, entradas de lanzamiento firmadas; La importación de URL recupera una URL pública solicitada por el usuario; y OIDC, SAML, OpenTelemetry, webhooks, almacenamiento compatible con S3 o integraciones similares configurados explícitamente se ponen en contacto con los destinos elegidos por el administrador. Las descargas de modelos en tiempo de ejecución están deshabilitadas de forma predeterminada. Establezca `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1` solo para habilitar explícitamente las descargas automáticas de respaldo. Una [importación de paquete sin conexión](/es/guide/deployment) puede aprovisionar funciones de IA sin salida del modelo de tiempo de ejecución.
|
||||
|
||||
La única excepción son las **descargas de modelos de IA**: cuando un usuario instala un paquete de funciones de IA a través de la interfaz, el contenedor descarga el archivo del paquete precompilado desde Hugging Face, más unos pocos archivos de modelo individuales desde GitHub Releases, Google Storage y PyPI. Estas descargas ocurren una vez por paquete y se almacenan en el volumen `/data`.
|
||||
**Recomendaciones de cortafuegos:**
|
||||
|
||||
**Recomendaciones de firewall:**
|
||||
|
||||
| Escenario | Regla saliente |
|
||||
|Guión|regla de salida|
|
||||
|---|---|
|
||||
| Aislado de red (sin IA) | Bloquea todo el tráfico saliente del contenedor |
|
||||
| Se necesitan paquetes de IA | Permite HTTPS a `huggingface.co`, `*.xethub.hf.co`, `cdn-lfs.huggingface.co`, `github.com`, `objects.githubusercontent.com`, `storage.googleapis.com`, `pypi.org`, `files.pythonhosted.org` durante la instalación, luego bloquea |
|
||||
| Tras la instalación de IA | Bloquea todo el tráfico saliente, los modelos se almacenan en caché localmente |
|
||||
|Espacio de aire|Configure `SNAPOTTER_TELEMETRY=0` y `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0`, use la importación de paquetes de IA sin conexión, deshabilite la importación de URL y las integraciones externas, luego bloquee la salida|
|
||||
|Telemetría predeterminada|Permitir los puntos finales de PostHog y Sentry enumerados en los registros de su navegador/red; deshabilitar la telemetría si la política no lo permite|
|
||||
|Se necesitan paquetes de IA|Durante la instalación, permita HTTPS a `huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org`; luego bloquea esos hosts|
|
||||
|Integraciones externas|Permitir solo los destinos OIDC/SAML/OTLP/webhook/almacenamiento de objetos exactos configurados por el administrador|
|
||||
|
||||
Los archivos de paquete se sirven desde el almacenamiento Xet de Hugging Face, que transfiere a través de los endpoints `*.xethub.hf.co` en paralelo y es lo que hace rápidas las descargas de paquetes de varios GB. Si tu firewall permite `huggingface.co` pero bloquea `*.xethub.hf.co`, las instalaciones aún tienen éxito pero recurren a una descarga más lenta de un solo flujo, así que incluye los hosts de Xet en la lista de permitidos para mantenerte en la vía rápida. Las instalaciones completamente sin conexión pueden saltarse todo esto y usar la [Importación de paquetes sin conexión](/es/guide/deployment) en su lugar.
|
||||
Los archivos de paquetes se sirven desde el almacenamiento Xet de Hugging Face, que se transfiere a través de los puntos finales `*.xethub.hf.co` en paralelo y es lo que acelera las descargas de paquetes de varios GB. Si su firewall permite `huggingface.co` pero bloquea `*.xethub.hf.co`, las instalaciones aún se realizan correctamente pero recurren a una descarga de flujo único más lenta, por lo que debe incluir los hosts Xet en la lista de permitidos para permanecer en la ruta rápida. Las instalaciones completamente fuera de línea pueden omitir todo esto y usar [Importación de paquete sin conexión](/es/guide/deployment) en su lugar.
|
||||
|
||||
Para la configuración del proxy inverso (Nginx, Traefik, Caddy, túneles de Cloudflare), consulta la [guía de Despliegue](/es/guide/deployment#reverse-proxy).
|
||||
Para la configuración del proxy inverso (Nginx, Traefik, Caddy, Cloudflare Tunnels), consulte la [Guía de implementación](/es/guide/deployment#reverse-proxy).
|
||||
|
||||
## Secretos de Docker {#docker-secrets}
|
||||
|
||||
@@ -257,83 +167,101 @@ Para el dimensionamiento de recursos, consulta [Requisitos de hardware](/es/guid
|
||||
|
||||
## Copia de seguridad y recuperación {#backup-and-recovery}
|
||||
|
||||
El estado persistente se divide entre dos volúmenes:
|
||||
La pila de producción Compose define cuatro volúmenes. Detenga el ingreso y deje que finalicen los trabajos activos antes de realizar una copia de seguridad coordinada para que PostgreSQL, Redis y el estado del archivo describan el mismo momento.
|
||||
|
||||
| Volumen | Contenido | ¿Crítico? |
|
||||
|Volumen|Contenido|Tratamiento de recuperación|
|
||||
|---|---|---|
|
||||
| `SnapOtter-pgdata` | Base de datos PostgreSQL (usuarios, ajustes, canalizaciones, trabajos, registro de auditoría) | Sí |
|
||||
| `/data` (volumen de la app) | Archivos subidos por usuarios, modelos de IA, venv de Python | Parcialmente (ver abajo) |
|
||||
|`SnapOtter-pgdata`|Usuarios, configuraciones, canalizaciones, trabajos, metadatos de archivos y registros de auditoría de PostgreSQL|Crítico; utilice un volcado lógico a prueba de fallos para una recuperación portátil|
|
||||
|`SnapOtter-data`|Objetos de biblioteca guardados, registros y estado de IA (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|Haga una copia de seguridad de todo el volumen; para ahorrar espacio, omita deliberadamente todo el estado de la IA y reinstale sus paquetes|
|
||||
|`SnapOtter-redisdata`|Redis AOF para un estado de cola BullMQ duradero|Haga una copia de seguridad después de pausar la aplicación y forzar `SAVE`; requerido para reanudar el trabajo en cola exactamente|
|
||||
|`SnapOtter-workspace`|Claves de almacenamiento temporal de objetos (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|No realice copias de seguridad después de que todos los trabajos se hayan agotado o cancelado; nunca lo descartes mientras los trabajos estén activos|
|
||||
|
||||
Dentro del volumen `/data`:
|
||||
|
||||
| Ruta | Contenido | ¿Crítico? |
|
||||
|---|---|---|
|
||||
| `/data/uploads/`, `/data/outputs/` | Archivos de usuario y resultados de procesamiento | Sí |
|
||||
| `/data/ai/` | Archivos de modelo de IA descargados | No (redescargables) |
|
||||
| `/data/venv/` | Entorno virtual de Python | No (se reconstruye al iniciar) |
|
||||
Compose normalmente antepone los nombres de los volúmenes al nombre del proyecto. Resuelva el volumen de origen real desde el contenedor montado en lugar de asumir que un nombre para mostrar como `SnapOtter-data` es el nombre del volumen de Docker.
|
||||
|
||||
### Copia de seguridad de la base de datos {#database-backup}
|
||||
|
||||
Usa `pg_dump` para hacer una copia de seguridad de la base de datos mientras la pila está en ejecución:
|
||||
Utilice el formato de archivo personalizado de PostgreSQL y verifique el archivo antes de considerar que la copia de seguridad está completa:
|
||||
|
||||
```bash
|
||||
# Dump the database
|
||||
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql
|
||||
docker exec SnapOtter-postgres \
|
||||
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
|
||||
test -s snapotter.dump
|
||||
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
|
||||
|
||||
# Restore into a fresh database
|
||||
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter
|
||||
# Restore only into a fresh/disposable target first; any SQL error fails the command.
|
||||
docker exec -i SnapOtter-postgres \
|
||||
pg_restore --exit-on-error --clean --if-exists --no-owner \
|
||||
-U snapotter -d snapotter < snapotter.dump
|
||||
```
|
||||
|
||||
Como alternativa, detén la pila y haz una instantánea del volumen `SnapOtter-pgdata`:
|
||||
Pruebe cada copia de seguridad restaurándola en una pila aislada, verificando los registros de la base de datos y las sumas de verificación de los archivos e iniciando la aplicación. El `tests/qa/backup-restore-drill.sh` del repositorio automatiza esa puerta de liberación contra un `QA_IMAGE` explícito.
|
||||
|
||||
Si su plataforma toma instantáneas de volúmenes coherentes con las fallas, primero detenga toda la pila y tome instantáneas de todos los volúmenes críticos como un solo conjunto. Una copia sin formato del directorio de datos de PostgreSQL desde un contenedor en ejecución no es una copia de seguridad lógica compatible.
|
||||
|
||||
### Copia de seguridad de archivos y colas {#file-and-queue-backup}
|
||||
|
||||
Pause la aplicación antes de capturar volúmenes de archivos y colas. Utilice `docker inspect` para resolver el nombre del volumen real, forzar a Redis a conservar su estado actual y archivar conservando la propiedad y los permisos:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
|
||||
docker stop SnapOtter
|
||||
docker exec SnapOtter-redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning SAVE
|
||||
docker stop SnapOtter-redis
|
||||
|
||||
DATA_VOLUME="$(docker inspect SnapOtter --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
|
||||
REDIS_VOLUME="$(docker inspect SnapOtter-redis --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
|
||||
|
||||
install -d -m 700 backup
|
||||
docker run --rm -v "$DATA_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
|
||||
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-data.tar.gz -C /source .
|
||||
docker run --rm -v "$REDIS_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
|
||||
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-redis.tar.gz -C /source .
|
||||
sha256sum backup/snapotter-*.tar.gz > backup/SHA256SUMS
|
||||
```
|
||||
|
||||
### Copia de seguridad de los archivos de usuario {#user-files-backup}
|
||||
|
||||
```bash
|
||||
# Snapshot the app data volume (excluding re-downloadable AI models)
|
||||
docker run --rm -v SnapOtter-data:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-files.tar.gz \
|
||||
--exclude='ai' --exclude='venv' -C /data .
|
||||
```
|
||||
|
||||
Los modelos de IA suman hasta unos 24 GB en total entre todos los paquetes. Como son redescargables, excluye `/data/ai/` y `/data/venv/` de las copias de seguridad para ahorrar espacio. Solo la base de datos y los archivos de usuario son críticos.
|
||||
Reinicie Redis antes de la aplicación. Si excluye intencionalmente `/data/ai`, elimine todo el subárbol AI en lugar de conservar un registro `installed.json` sin sus modelos o entorno virtual. Mantenga los archivos de respaldo cifrados, con acceso controlado y separados del host que ejecuta SnapOtter.
|
||||
|
||||
## Artefactos de cumplimiento {#compliance-artifacts}
|
||||
|
||||
Cada versión de SnapOtter incluye los siguientes artefactos de seguridad:
|
||||
|
||||
| Artefacto | Formato | Dónde encontrarlo |
|
||||
| Artefacto | Formato | donde encontrarlo |
|
||||
|---|---|---|
|
||||
| SBOM (CycloneDX) | JSON | Recurso de la [versión de GitHub](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.cdx.json` |
|
||||
| SBOM (SPDX) | JSON | Recurso de la [versión de GitHub](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.spdx.json` |
|
||||
| Escaneo de vulnerabilidades | Trivy JSON | Recurso de la [versión de GitHub](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-trivy.json` |
|
||||
| Escaneo de vulnerabilidades | SARIF | Pestaña [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) |
|
||||
| Análisis estático | CodeQL (JS/TS + Python) | Pestaña [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), se ejecuta semanalmente + por PR |
|
||||
| Revisión de dependencias | Nativo de GitHub | Comprobación por PR, falla ante adiciones de alta gravedad |
|
||||
| Auditoría de dependencias de Python | pip-audit | Registro de ejecución de CI en cada push |
|
||||
| Liberar enlace de asunto | Certificación canónica JSON + GitHub | [Lanzamiento GitHub](https://github.com/snapotter-hq/SnapOtter/releases) activo: `snapotter-v{version}-release-subjects.json` |
|
||||
| Archivo SBOM | CycloneDX y SPDX JSON | Activos de lanzamiento: `snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
|
||||
| Imagen SBOM | CycloneDX y SPDX JSON | Activos de lanzamiento: `snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
|
||||
| Escaneos de vulnerabilidad | Trivy JSON | Liberar activos con prefijos `archive-linux-{arch}` o `image-linux-{arch}` coincidentes |
|
||||
| Escaneo de vulnerabilidad | SARIF | Pestaña [Seguridad GitHub](https://github.com/snapotter-hq/SnapOtter/security) |
|
||||
| Análisis estático | CodeQL (JS/TS + Python) | Pestaña [Seguridad GitHub](https://github.com/snapotter-hq/SnapOtter/security), se ejecuta semanalmente + por PR |
|
||||
| Revisión de dependencia | GitHub nativo | Verificación por PR, falla en adiciones de alta gravedad |
|
||||
| Auditoría de dependencia Python | pip-audit | Registro de ejecución de CI en cada pulsación |
|
||||
| Política de seguridad | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) en el repositorio |
|
||||
| Actualizaciones de dependencias | Dependabot | PRs semanales automatizados para npm, pip, Docker, Actions |
|
||||
| Actualizaciones de dependencia | Dependabot | PR semanales automatizados para npm, pip, Docker, acciones |
|
||||
|
||||
**Ejecutar tu propio escaneo:**
|
||||
**Ejecutando tu propio escaneo:**
|
||||
|
||||
Descarga el SBOM de la versión y escanéalo con la herramienta que prefieras:
|
||||
Descargue el manifiesto sujeto a la versión y verifique que haya sido atestiguado por el flujo de trabajo de la versión:
|
||||
|
||||
```bash
|
||||
gh attestation verify snapotter-v2.1.0-release-subjects.json \
|
||||
--repo snapotter-hq/SnapOtter \
|
||||
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
|
||||
```
|
||||
|
||||
El manifiesto registra `releaseTag`, `releaseCommit` y `workflowTriggerCommit` por separado. Verifique que `releaseCommit` sea la confirmación extraída de la etiqueta inmutable, luego verifique el resumen SHA-256 del archivo, imagen, SBOM o escaneo que consume con su entrada en `subjects`. Esta distinción es intencional: verificar una confirmación de versión recién creada no cambia la identidad de la confirmación en la credencial OIDC del flujo de trabajo.
|
||||
|
||||
También puedes escanear un SBOM descargado o la imagen directamente:
|
||||
|
||||
```bash
|
||||
# Scan with Grype using the CycloneDX SBOM
|
||||
grype sbom:snapotter-v1.17.2-sbom.cdx.json
|
||||
grype sbom:snapotter-v2.1.0-image-linux-amd64-sbom.cdx.json
|
||||
|
||||
# Scan with Trivy using the SPDX SBOM
|
||||
trivy sbom snapotter-v1.17.2-sbom.spdx.json
|
||||
trivy sbom snapotter-v2.1.0-image-linux-amd64-sbom.spdx.json
|
||||
|
||||
# Scan the Docker image directly
|
||||
trivy image snapotter/snapotter:1.17.2
|
||||
trivy image snapotter/snapotter:2.1.0
|
||||
```
|
||||
|
||||
::: info
|
||||
El SBOM y el escaneo de vulnerabilidades reflejan la imagen exacta publicada para esa versión. Los paquetes de modelos de IA instalados tras el despliegue no se incluyen en el SBOM, ya que se descargan en tiempo de ejecución.
|
||||
::: info
|
||||
La imagen SBOMs y los escaneos reflejan la imagen exacta de la arquitectura específica publicada para esa versión. El archivo SBOMs y los análisis describen el archivo prediseñado por separado. Los paquetes de modelos AI instalados después de la implementación no se incluyen en estos SBOMs porque se descargan en tiempo de ejecución.
|
||||
:::
|
||||
|
||||
@@ -11,7 +11,7 @@ SnapOtter procesa archivos en cinco modalidades: imagen, vídeo, audio, PDF y ar
|
||||
|
||||
## Formatos de imagen {#image-formats}
|
||||
|
||||
SnapOtter admite más de 55 formatos de imagen para entrada y 13 formatos para salida.
|
||||
SnapOtter admite más de 55 formatos de imagen para entrada y 17 formatos para salida.
|
||||
|
||||
## Formatos de entrada {#input-formats}
|
||||
|
||||
@@ -104,7 +104,7 @@ SnapOtter admite más de 55 formatos de imagen para entrada y 13 formatos para s
|
||||
| PAM | .pam | Sharp (nativo) | Mapa arbitrario |
|
||||
| PFM | .pfm | Sharp (nativo) | Mapa de coma flotante |
|
||||
|
||||
## Formatos de salida (13) {#output-formats-13}
|
||||
## Formatos de salida (17) {#output-formats-13}
|
||||
|
||||
| Formato | Codificador | Control de calidad | Disponible en |
|
||||
|--------|---------|----------------|-------------|
|
||||
@@ -121,6 +121,10 @@ SnapOtter admite más de 55 formatos de imagen para entrada y 13 formatos para s
|
||||
| ICO | CLI ImageMagick | Sin pérdidas | Herramienta de conversión |
|
||||
| JP2 | CLI opj_compress | Ratio de compresión | Herramienta de conversión |
|
||||
| QOI | Códec en línea | Sin pérdidas | Herramienta de conversión |
|
||||
| PSD | CLI ImageMagick | Sin pérdidas | Herramienta de conversión |
|
||||
| PPM | CLI ImageMagick | Sin pérdidas | Herramienta de conversión |
|
||||
| EPS | CLI ImageMagick | Sin pérdidas | Herramienta de conversión |
|
||||
| TGA | CLI ImageMagick | Sin pérdidas | Herramienta de conversión |
|
||||
|
||||
## Formatos de vídeo {#video-formats}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Gestiona usuarios, roles integrados y personalizados, permisos, claves de API, equipos, sesiones y el registro de auditoría en SnapOtter."
|
||||
i18n_source_hash: 5e28af686c96
|
||||
i18n_source_hash: bea8955f3aff
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: a2ceae4b73a7
|
||||
i18n_output_hash: c5ce9d432c7d
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Usuarios, roles y permisos {#users-roles-permissions}
|
||||
@@ -82,12 +83,12 @@ Los 17 permisos. Control total sobre la instancia.
|
||||
| `pipelines:all` | Ver y gestionar los pipelines de todos los usuarios |
|
||||
| `settings:read` | Ver la configuración de la instancia |
|
||||
| `settings:write` | Modificar la configuración de la instancia |
|
||||
| `users:manage` | Crear, actualizar y eliminar cuentas de usuario |
|
||||
| `users:manage` | Crear y administrar cuentas de usuario dentro de los límites de autoridad del actor. |
|
||||
| `teams:manage` | Crear, actualizar y eliminar equipos |
|
||||
| `features:manage` | Instalar y gestionar bundles de funciones de IA |
|
||||
| `system:health` | Acceder a los endpoints de estado y disponibilidad |
|
||||
| `audit:read` | Ver el registro de auditoría y listar roles |
|
||||
| `compliance:manage` | Gestionar el ciclo de vida de RGPD y las funciones de cumplimiento |
|
||||
| `compliance:manage` | Gestionar el ciclo de vida del RGPD y las funciones de cumplimiento; Las operaciones destructivas del usuario siguen estando limitadas por la autoridad. |
|
||||
| `webhooks:manage` | Configurar webhooks salientes |
|
||||
| `security:manage` | Gestionar la configuración de seguridad (lista de IP permitidas, imposición de SSO) |
|
||||
|
||||
@@ -110,15 +111,17 @@ curl -X POST http://localhost:1349/api/v1/roles \
|
||||
|
||||
Los nombres de rol deben tener entre 2 y 30 caracteres, alfanuméricos en minúscula con guiones y guiones bajos.
|
||||
|
||||
### Permisos reservados para administradores {#admin-reserved-permissions}
|
||||
### Límites de administración delegada {#delegated-administration-boundaries}
|
||||
|
||||
Tres permisos están reservados para los roles integrados y no pueden asignarse a roles personalizados:
|
||||
Los 17 permisos se pueden delegar a través de roles personalizados, pero un permiso administrativo no hace que ese rol sea equivalente al rol integrado `admin`. Las mutaciones de usuario autorizadas por `users:manage`, las operaciones destructivas autorizadas por `compliance:manage` y la gestión de roles personalizados autorizada por `security:manage` están limitadas por la autoridad actual del actor:
|
||||
|
||||
- `compliance:manage`
|
||||
- `webhooks:manage`
|
||||
- `security:manage`
|
||||
- Los roles integrados siguen `admin` > `editor` > `user`; Los roles personalizados están debajo de los roles integrados.
|
||||
- Los permisos del objetivo deben estar contenidos en los permisos **efectivos** del actor. Por lo tanto, una clave API con ámbito no puede ejercer permisos omitidos en su ámbito.
|
||||
- El acceso a la herramienta de un rol objetivo debe estar contenido en el acceso a la herramienta del propio actor.
|
||||
- Una cuenta deshabilitada se compara con su función original cuando esa función se registra como `disabled:<original-role>`.
|
||||
- Eliminar un rol personalizado también requiere autoridad para asignar el respaldo `user` incorporado; los miembros discapacitados permanecen discapacitados como `disabled:user`.
|
||||
|
||||
La API de roles rechaza cualquier solicitud que incluya estos permisos. Solo el rol integrado `admin` tiene acceso a ellos.
|
||||
Las credenciales y la configuración globales son más estrictas: emitir o revocar el token SCIM e importar la configuración de la instancia requieren la función `admin` incorporada con autoridad de administrador efectiva completa.
|
||||
|
||||
### Permisos a nivel de herramienta {#tool-level-permissions}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user