Compare commits
No commits in common. "ac2806e2f6244e97af0cb8f8432243ae3db0445a" and "62961f0db4f6b85855a6dea43f34a948e3d06a86" have entirely different histories.
ac2806e2f6
...
62961f0db4
50
.env.example
50
.env.example
@ -1,50 +0,0 @@
|
||||
# .env.example para ejecutar LibreTime con Docker Compose en macOS
|
||||
# Copia este archivo a .env y modifica las contraseñas/valores antes de levantar el stack.
|
||||
|
||||
# Postgres
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=libretime
|
||||
POSTGRES_USER=libretime
|
||||
POSTGRES_PASSWORD=change_me_postgres
|
||||
|
||||
# RabbitMQ
|
||||
RABBITMQ_HOST=rabbitmq
|
||||
RABBITMQ_PORT=5672
|
||||
RABBITMQ_DEFAULT_VHOST=/libretime
|
||||
RABBITMQ_DEFAULT_USER=libretime
|
||||
RABBITMQ_DEFAULT_PASS=change_me_rabbit
|
||||
|
||||
# Icecast
|
||||
ICECAST_SOURCE_PASSWORD=change_me_icecast
|
||||
ICECAST_ADMIN_PASSWORD=change_me_icecast_admin
|
||||
ICECAST_RELAY_PASSWORD=change_me_icecast_relay
|
||||
ICECAST_ADMIN_USER=admin
|
||||
ICECAST_HOSTNAME=localhost
|
||||
|
||||
# LibreTime
|
||||
LIBRETIME_GENERAL_PUBLIC_URL=http://localhost:8000
|
||||
LIBRETIME_DEBUG=true
|
||||
LIBRETIME_VERSION=latest
|
||||
POSTGRES_USER=libretime
|
||||
POSTGRES_PASSWORD=libretime
|
||||
POSTGRES_DB=libretime
|
||||
|
||||
RABBITMQ_DEFAULT_VHOST=/libretime
|
||||
RABBITMQ_DEFAULT_USER=libretime
|
||||
RABBITMQ_DEFAULT_PASS=libretime
|
||||
|
||||
LIBRETIME_VERSION=4.5
|
||||
LIBRETIME_GENERAL_PUBLIC_URL=http://localhost:8080
|
||||
|
||||
# Claves para la aplicación (se generan automáticamente si no se proveen)
|
||||
LIBRETIME_API_KEY=
|
||||
LIBRETIME_SECRET_KEY=
|
||||
|
||||
NGINX_PORT=8080
|
||||
ICECAST_PORT=8000
|
||||
|
||||
# Icecast passwords (cambiar en producción)
|
||||
ICECAST_SOURCE_PASSWORD=changeme
|
||||
ICECAST_ADMIN_PASSWORD=changeme
|
||||
ICECAST_RELAY_PASSWORD=changeme
|
||||
@ -1,45 +0,0 @@
|
||||
# Ejecutar LibreTime en macOS con Docker Desktop
|
||||
|
||||
Pasos rápidos para desarrollo local usando Docker Desktop en macOS:
|
||||
|
||||
1. Instalar Docker Desktop (si no está instalado)
|
||||
|
||||
```zsh
|
||||
brew install --cask docker
|
||||
open --background -a Docker
|
||||
# Espera a que Docker inicie (arriba a la derecha aparece el icono)
|
||||
```
|
||||
|
||||
2. Asignar recursos en Docker Desktop: al menos 2 CPUs y 4 GB RAM
|
||||
|
||||
3. Copiar `.env.example` a `.env` y editar contraseñas/variables
|
||||
|
||||
```zsh
|
||||
cp .env.example .env
|
||||
# Edita .env y asigna contraseñas seguras para POSTGRES_PASSWORD y RABBITMQ_DEFAULT_PASS
|
||||
```
|
||||
|
||||
4. Levantar el stack (usando el compose preparado para EasyPanel que es env-driven):
|
||||
|
||||
```zsh
|
||||
docker compose -f docker-compose.easypanel.yml up -d
|
||||
```
|
||||
|
||||
5. Verificar estado y logs:
|
||||
|
||||
```zsh
|
||||
docker compose -f docker-compose.easypanel.yml ps
|
||||
docker compose -f docker-compose.easypanel.yml logs -f config-generator
|
||||
docker compose -f docker-compose.easypanel.yml logs -f api
|
||||
```
|
||||
|
||||
6. Si necesitas entrar al contenedor para correr migraciones:
|
||||
|
||||
```zsh
|
||||
docker compose -f docker-compose.easypanel.yml exec api python manage.py migrate --noinput
|
||||
docker compose -f docker-compose.easypanel.yml exec api python manage.py createsuperuser
|
||||
```
|
||||
|
||||
Notas específicas macOS:
|
||||
- Usa volúmenes nombrados (ya configurado en el compose) para evitar I/O lento por bind mounts.
|
||||
- Si el contenedor muestra errores relacionados con recursos, aumenta CPU/RAM en Docker Desktop.
|
||||
@ -1,37 +1,3 @@
|
||||
README - EasyPanel / Git repo setup
|
||||
|
||||
Este archivo describe cómo usar `docker-compose.easypanel.yml` desde EasyPanel.
|
||||
|
||||
Principio: No almacenar secretos en el repositorio. EasyPanel debe inyectar los valores
|
||||
mediante variables de entorno o secrets en el formulario del panel.
|
||||
|
||||
Variables (mínimas recomendadas)
|
||||
- POSTGRES_HOST (por defecto: postgres)
|
||||
- POSTGRES_PORT (por defecto: 5432)
|
||||
- POSTGRES_DB (por defecto: libretime)
|
||||
- POSTGRES_USER (por defecto: libretime)
|
||||
- POSTGRES_PASSWORD (REQUIRED)
|
||||
- RABBITMQ_HOST (por defecto: rabbitmq)
|
||||
- RABBITMQ_PORT (por defecto: 5672)
|
||||
- RABBITMQ_DEFAULT_VHOST (por defecto: /libretime)
|
||||
- RABBITMQ_DEFAULT_USER (por defecto: libretime)
|
||||
- RABBITMQ_DEFAULT_PASS (REQUIRED)
|
||||
- ICECAST_SOURCE_PASSWORD (opcional)
|
||||
- ICECAST_ADMIN_PASSWORD (opcional)
|
||||
- LIBRETIME_GENERAL_PUBLIC_URL (opcional)
|
||||
- LIBRETIME_API_KEY (opcional)
|
||||
|
||||
Cómo funciona
|
||||
- Al crear la aplicación en EasyPanel, configure estas variables como "Environment Variables" o "Secrets".
|
||||
- El servicio `config-generator` ejecuta `tools/easypanel-config-generator.sh` y escribe
|
||||
la configuración final en el volumen `libretime_config` en `/config/config.yml`.
|
||||
- Los contenedores `api`, `legacy`, `playout`, `worker` y `analyzer` montan ese volumen
|
||||
en `/etc/libretime` y leen la configuración desde `/etc/libretime/config.yml`.
|
||||
|
||||
Notas
|
||||
- Asegúrese de que las variables sensibles (POSTGRES_PASSWORD, RABBITMQ_DEFAULT_PASS) se guarden
|
||||
como secretos en EasyPanel y no como variables visibles en el repo.
|
||||
- Si desea personalizar más opciones, modifique `tools/easypanel-config-generator.sh`.
|
||||
# LibreTime para EasyPanel
|
||||
|
||||
Esta guía te ayudará a desplegar LibreTime en EasyPanel usando Docker Compose.
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
LibreTime - Instalación Docker sencilla
|
||||
|
||||
Objetivo
|
||||
--------
|
||||
Proveer un conjunto mínimo de ficheros para que un usuario final pueda levantar LibreTime con Docker Compose sin tocar demasiadas cosas.
|
||||
|
||||
Archivos añadidos
|
||||
- `docker-compose.simple.yml` - Compose reducido y comentado.
|
||||
- `.env.example` - Ejemplo de variables de entorno.
|
||||
- `scripts/generate-config.sh` - Genera `config.local.yml` desde variables de entorno.
|
||||
- `start.sh` - Flujo recomendado: cargar .env, generar config, iniciar infra y aplicar migraciones.
|
||||
|
||||
Cómo usar
|
||||
---------
|
||||
1) Copiar `.env.example` a `.env` y editar las contraseñas si lo desea.
|
||||
|
||||
2) Generar `config.local.yml` (opcional — `start.sh` lo hace):
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
sh ./scripts/generate-config.sh ./config.local.yml
|
||||
```
|
||||
|
||||
3) Ejecutar el flujo automático:
|
||||
|
||||
```sh
|
||||
sh ./start.sh
|
||||
```
|
||||
|
||||
Esto levantará Postgres y RabbitMQ, aplicará las migraciones (necesario la primera vez) y luego arrancará el resto de servicios. El sitio estará disponible en `http://localhost:8080` por defecto.
|
||||
|
||||
|
||||
Notas y recomendaciones
|
||||
|
||||
- Para producción cambie todas las contraseñas por valores fuertes en `.env`.
|
||||
- Si ya existe un volumen de Postgres creado con otra contraseña, borre el volumen antes de usar las nuevas credenciales: `docker compose -f docker-compose.simple.yml down -v`.
|
||||
- Puede ejecutar migraciones manualmente con:
|
||||
|
||||
```sh
|
||||
docker compose -f docker-compose.simple.yml run --rm migrate
|
||||
```
|
||||
|
||||
Corregir desajuste de contraseña (opción A)
|
||||
-----------------------------------------
|
||||
Si al hacer peticiones la API devuelve 500 por errores de conexión a la base de datos, normalmente significa que la contraseña de Postgres en `.env` y la que está escrita en `config.local.yml` no coinciden. Para arreglarlo manualmente:
|
||||
|
||||
1. Edita `.env` y establece `POSTGRES_PASSWORD` con la contraseña que quieras usar para Postgres (ejemplo seguro).
|
||||
|
||||
2. Regenera `config.local.yml` a partir de `.env` (esto sincroniza la contraseña usada por la app con la de Postgres):
|
||||
|
||||
```sh
|
||||
cp .env.example .env # si aún no tienes .env
|
||||
sh ./scripts/generate-config.sh ./config.local.yml
|
||||
```
|
||||
|
||||
3. Reinicia el servicio API para que recargue el archivo de configuración montado:
|
||||
|
||||
```sh
|
||||
docker compose -f docker-compose.simple.yml restart api
|
||||
```
|
||||
|
||||
4. Prueba internamente en el contenedor API y desde Nginx:
|
||||
|
||||
```sh
|
||||
docker exec -it $(docker compose -f docker-compose.simple.yml ps -q api) \
|
||||
curl -sS http://127.0.0.1:9001/api/v2/info
|
||||
|
||||
curl -sS http://localhost:8080/api/v2/info
|
||||
```
|
||||
|
||||
Si ambas llamadas devuelven HTTP 200 con JSON (por ejemplo {"station_name":"LibreTime"}) entonces el desajuste está resuelto.
|
||||
|
||||
Si persiste el problema, considera eliminar el volumen de Postgres (perderás datos de desarrollo) y volver a inicializar:
|
||||
|
||||
```sh
|
||||
docker compose -f docker-compose.simple.yml down -v
|
||||
sh ./start.sh
|
||||
```
|
||||
|
||||
Nota sobre `start.sh`
|
||||
---------------------
|
||||
`start.sh` genera `config.local.yml` desde las variables de entorno y, si detecta que `POSTGRES_PASSWORD` está definido en `.env`, actualizará automáticamente la entrada `database.password` dentro de `config.local.yml` para que coincida. Esto evita desajustes entre la contraseña usada por el contenedor Postgres y la que usa la aplicación. Si prefieres no sobrescribir `config.local.yml`, edita manualmente `.env` y/o `config.local.yml` antes de ejecutar `start.sh`.
|
||||
|
||||
Antes de sobrescribir `config.local.yml`, `start.sh` crea una copia de seguridad con prefijo `config.local.yml.bak.YYYYMMDDHHMMSS` en la raíz del proyecto.
|
||||
@ -1,24 +0,0 @@
|
||||
general:
|
||||
public_url: "http://localhost:8080"
|
||||
api_key: "bKUFZWDmjXUdHWcJpHbOdSqeXyqwsYwtRWmcnqBw"
|
||||
secret_key: "ZlHiWa87oPXvWlHfF9w9AH0WATtGJXONmwv4RVPn"
|
||||
|
||||
database:
|
||||
name: "libretime"
|
||||
user: "libretime"
|
||||
password: "libretime"
|
||||
host: "postgres"
|
||||
port: 5432
|
||||
|
||||
rabbitmq:
|
||||
host: "rabbitmq"
|
||||
user: "libretime"
|
||||
password: "libretime"
|
||||
vhost: "/libretime"
|
||||
|
||||
icecast:
|
||||
source_password: "changeme"
|
||||
admin_password: "changeme"
|
||||
relay_password: "changeme"
|
||||
admin_user: "admin"
|
||||
hostname: "localhost"
|
||||
@ -1,24 +0,0 @@
|
||||
general:
|
||||
public_url: "http://localhost:8080"
|
||||
api_key: "dJA8shP8Epyx5TX5Ye4EYxCJ2RaGCBP2Dl8lcv6T"
|
||||
secret_key: "PnEuyp3Ibko6O6tAArchoEHR7SNxH5XvFrp6xi9x"
|
||||
|
||||
database:
|
||||
name: "libretime"
|
||||
user: "libretime"
|
||||
password: "libretime"
|
||||
host: "postgres"
|
||||
port: 5432
|
||||
|
||||
rabbitmq:
|
||||
host: "rabbitmq"
|
||||
user: "libretime"
|
||||
password: "libretime"
|
||||
vhost: "/libretime"
|
||||
|
||||
icecast:
|
||||
source_password: "changeme"
|
||||
admin_password: "changeme"
|
||||
relay_password: "changeme"
|
||||
admin_user: "admin"
|
||||
hostname: "localhost"
|
||||
@ -1,25 +0,0 @@
|
||||
general:
|
||||
public_url: "http://localhost:8080"
|
||||
api_key: "dJA8shP8Epyx5TX5Ye4EYxCJ2RaGCBP2Dl8lcv6T"
|
||||
secret_key: "PnEuyp3Ibko6O6tAArchoEHR7SNxH5XvFrp6xi9x"
|
||||
|
||||
database:
|
||||
engine: postgres
|
||||
name: "libretime"
|
||||
user: "libretime"
|
||||
password: "libretime"
|
||||
host: "postgres"
|
||||
port: 5432
|
||||
|
||||
rabbitmq:
|
||||
host: "rabbitmq"
|
||||
user: "libretime"
|
||||
password: "libretime"
|
||||
vhost: "/libretime"
|
||||
|
||||
icecast:
|
||||
source_password: "changeme"
|
||||
admin_password: "changeme"
|
||||
relay_password: "changeme"
|
||||
admin_user: "admin"
|
||||
hostname: "localhost"
|
||||
@ -1,13 +1,6 @@
|
||||
version: "3.9"
|
||||
|
||||
# LibreTime Docker Compose (EasyPanel-ready)
|
||||
# Esta versión está pensada para usarse desde un repositorio en EasyPanel.
|
||||
# Principios:
|
||||
# - No almacenar secretos en archivos del repo: EasyPanel proporcionará los valores
|
||||
# mediante variables/secretos de entorno.
|
||||
# - Un servicio `config-generator` genera `/config/config.yml` dentro del volumen
|
||||
# `libretime_config` a partir de variables de entorno. Los servicios montan ese
|
||||
# volumen en `/etc/libretime`.
|
||||
# LibreTime Docker Compose para EasyPanel
|
||||
# Basado en el docker-compose oficial de LibreTime
|
||||
# Optimizado para despliegue en EasyPanel
|
||||
|
||||
services:
|
||||
# Base de datos PostgreSQL
|
||||
@ -33,7 +26,7 @@ services:
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-libretime}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "rabbitmq-diagnostics check_port_connectivity"]
|
||||
interval: 30s
|
||||
@ -42,7 +35,7 @@ services:
|
||||
|
||||
# API de LibreTime
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
@ -52,18 +45,16 @@ services:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
config-generator:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
LIBRETIME_DEBUG: ${LIBRETIME_DEBUG:-false}
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# Aplicación Legacy de LibreTime
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
@ -73,15 +64,13 @@ services:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
config-generator:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# Servidor web Nginx
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- legacy
|
||||
@ -90,7 +79,6 @@ services:
|
||||
- "${NGINX_PORT:-8080}:8080"
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
environment:
|
||||
- NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-auto}
|
||||
|
||||
@ -109,7 +97,7 @@ services:
|
||||
|
||||
# Servicio de playout
|
||||
playout:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
@ -118,14 +106,14 @@ services:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
# Liquidsoap para streaming
|
||||
liquidsoap:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-latest}
|
||||
command: /usr/local/bin/libretime-liquidsoap
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
@ -138,14 +126,14 @@ services:
|
||||
- "${LIQUIDSOAP_HARBOR_PORT:-8001}:8001"
|
||||
- "${LIQUIDSOAP_TELNET_PORT:-8002}:8002"
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
# Worker para tareas en background
|
||||
worker:
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
@ -154,14 +142,14 @@ services:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
# Analyzer para análisis de archivos de audio
|
||||
analyzer:
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
@ -170,25 +158,9 @@ services:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# (No se incluye servicio composer; seguir método Docker estándar de LibreTime)
|
||||
|
||||
# Generador de configuración (escrito por el panel desde variables/secretos)
|
||||
config-generator:
|
||||
image: alpine:3.18
|
||||
restart: "no"
|
||||
entrypoint: ["/bin/sh","/tools/easypanel-config-generator.sh"]
|
||||
volumes:
|
||||
- libretime_config:/config
|
||||
- ./tools:/tools:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL","test -f /config/config.yml"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
# Volúmenes persistentes
|
||||
volumes:
|
||||
postgres_data:
|
||||
@ -197,8 +169,6 @@ volumes:
|
||||
driver: local
|
||||
libretime_playout:
|
||||
driver: local
|
||||
libretime_config:
|
||||
driver: local
|
||||
|
||||
# Red personalizada (opcional para EasyPanel)
|
||||
networks:
|
||||
|
||||
@ -1,168 +0,0 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-libretime}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-libretime}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-libretime}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-libretime}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-libretime}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "rabbitmq-diagnostics status"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
|
||||
# Servicio que aplica migraciones (uno-por-vez). Ejecutar como "docker compose run --rm migrate" o usar start.sh
|
||||
migrate:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
# start.sh ahora espera a Postgres/rabbit; aquí solo aplicamos migraciones
|
||||
command: "python3 /src/libretime_api/manage.py migrate --noinput"
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-libretime}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-libretime}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-libretime}
|
||||
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
environment:
|
||||
LIBRETIME_DEBUG: ${LIBRETIME_DEBUG:-false}
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
environment:
|
||||
LIBRETIME_CONFIG_FILEPATH: /etc/libretime/config.yml
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- legacy
|
||||
- api
|
||||
ports:
|
||||
- "${NGINX_PORT:-8080}:8080"
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
|
||||
icecast:
|
||||
image: ghcr.io/libretime/icecast:2.4.4
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${ICECAST_PORT:-8000}:8000"
|
||||
environment:
|
||||
ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-hackme}
|
||||
ICECAST_RELAY_PASSWORD: ${ICECAST_RELAY_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_USER: ${ICECAST_ADMIN_USER:-admin}
|
||||
ICECAST_HOSTNAME: ${ICECAST_HOSTNAME:-localhost}
|
||||
|
||||
playout:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_CONFIG_FILEPATH: /etc/libretime/config.yml
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
liquidsoap:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
command: /usr/local/bin/libretime-liquidsoap
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
ports:
|
||||
- "${LIQUIDSOAP_HARBOR_PORT:-8001}:8001"
|
||||
- "${LIQUIDSOAP_TELNET_PORT:-8002}:8002"
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_CONFIG_FILEPATH: /etc/libretime/config.yml
|
||||
|
||||
worker:
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
environment:
|
||||
LIBRETIME_CONFIG_FILEPATH: /etc/libretime/config.yml
|
||||
|
||||
analyzer:
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- rabbitmq
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# Generador de configuración para escribir config.yml en el volumen libretime_config
|
||||
config-generator:
|
||||
image: alpine:3.18
|
||||
restart: "no"
|
||||
entrypoint: ["/bin/sh","/tools/easypanel-config-generator.sh"]
|
||||
volumes:
|
||||
- libretime_config:/config
|
||||
- ./tools:/tools:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL","test -f /config/config.yml"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
environment:
|
||||
LIBRETIME_CONFIG_FILEPATH: /etc/libretime/config.yml
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
libretime_storage:
|
||||
driver: local
|
||||
libretime_playout:
|
||||
driver: local
|
||||
|
||||
# Volumen para la configuración generada por el panel
|
||||
libretime_config:
|
||||
driver: local
|
||||
|
||||
@ -19,7 +19,7 @@ services:
|
||||
test: nc -z 127.0.0.1 5672
|
||||
|
||||
playout:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-latest}
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
@ -32,7 +32,7 @@ services:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
|
||||
liquidsoap:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-latest}
|
||||
command: /usr/local/bin/libretime-liquidsoap
|
||||
init: true
|
||||
ulimits:
|
||||
@ -49,7 +49,7 @@ services:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
|
||||
analyzer:
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-latest}
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
@ -62,7 +62,7 @@ services:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
|
||||
worker:
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-latest}
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
@ -74,7 +74,7 @@ services:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: http://nginx:8080
|
||||
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-latest}
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
@ -86,7 +86,7 @@ services:
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-latest}
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
@ -98,7 +98,7 @@ services:
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-latest}
|
||||
ports:
|
||||
- 8080:8080
|
||||
depends_on:
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
|
||||
root /var/www/html/public;
|
||||
|
||||
index index.php index.html index.htm;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_buffers 64 4K;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
#try_files $uri =404;
|
||||
try_files $fastcgi_script_name =404;
|
||||
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
set $path_info $fastcgi_path_info;
|
||||
fastcgi_param PATH_INFO $path_info;
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_index index.php;
|
||||
fastcgi_pass legacy:9000;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
# Proxy todas las rutas /api (excepto /api/_media) a la API Python
|
||||
location ^~ /api/_media {
|
||||
internal;
|
||||
alias /srv/libretime;
|
||||
}
|
||||
|
||||
# Compatibilidad: interceptar /api/version antes de la copia proxy general
|
||||
# Usamos ^~ para dar prioridad sobre la regla general /api/
|
||||
location ^~ /api/version {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
# Variante con slash final
|
||||
location ^~ /api/version/ {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
# Compatibilidad: rutas legacy que deben ser manejadas por el código PHP
|
||||
# En la configuración original, peticiones /api/* que no eran /api/v2
|
||||
# caían en el enrutador PHP (try_files -> /index.php). Restauramos
|
||||
# ese comportamiento para endpoints concretos usados por playout.
|
||||
location = /api/register-component {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location = /api/register-component/ {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
# Proxy por defecto para todas las rutas /api/ (excepto las interceptadas arriba)
|
||||
location /api/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
|
||||
# Soporte para peticiones antiguas específicas a /api/v2 o /api/browser
|
||||
location ~ ^/api/(v2|browser) {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@ -1,53 +0,0 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_USER: libretime
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: libretime
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-alpine
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: /libretime
|
||||
RABBITMQ_DEFAULT_USER: libretime
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
environment:
|
||||
- LIBRETIME_GENERAL_PUBLIC_URL=${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
- LIBRETIME_CONFIG_FROM_ENV=true
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}
|
||||
environment:
|
||||
- LIBRETIME_CONFIG_FROM_ENV=true
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- legacy
|
||||
- api
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
libretime_storage:
|
||||
@ -1,72 +0,0 @@
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
|
||||
root /var/www/html/public;
|
||||
|
||||
index index.php index.html index.htm;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_buffers 64 4K;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
try_files $fastcgi_script_name =404;
|
||||
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
set $path_info $fastcgi_path_info;
|
||||
fastcgi_param PATH_INFO $path_info;
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_index index.php;
|
||||
fastcgi_pass legacy:9000;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ^~ /api/_media {
|
||||
internal;
|
||||
alias /srv/libretime;
|
||||
}
|
||||
|
||||
location ^~ /api/version {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
location ^~ /api/version/ {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
location = /api/register-component {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location = /api/register-component/ {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
|
||||
location ~ ^/api/(v2|browser) {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
import { Template } from '@easypanel/template-sdk';
|
||||
|
||||
// This secure template generates the docker-compose and uses environment variables
|
||||
// so that secrets are not stored in files inside the project. Instead, config
|
||||
// is generated at container start from env vars injected by the panel.
|
||||
|
||||
const t: Template = {
|
||||
meta: require('./meta.yaml'),
|
||||
render: (values: any) => {
|
||||
const libVersion = values.libretime_version || '4.5';
|
||||
const publicUrl = values.public_url || 'http://{{hostname}}:8080';
|
||||
|
||||
const compose = `version: '3.8'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_USER: libretime
|
||||
POSTGRES_PASSWORD: ${values.postgres_password ? `'${values.postgres_password}'` : "\"\""}
|
||||
POSTGRES_DB: libretime
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-alpine
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: /libretime
|
||||
RABBITMQ_DEFAULT_USER: libretime
|
||||
RABBITMQ_DEFAULT_PASS: ${values.rabbitmq_password ? `'${values.rabbitmq_password}'` : "\"\""}
|
||||
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${libVersion}
|
||||
environment:
|
||||
- LIBRETIME_GENERAL_PUBLIC_URL=${publicUrl}
|
||||
- LIBRETIME_CONFIG_FROM_ENV=true
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
|
||||
# composer not included; follow standard LibreTime docker installation
|
||||
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${libVersion}
|
||||
environment:
|
||||
- LIBRETIME_CONFIG_FROM_ENV=true
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime
|
||||
depends_on:
|
||||
- postgres
|
||||
- rabbitmq
|
||||
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${libVersion}
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- legacy
|
||||
- api
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
libretime_storage:
|
||||
`;
|
||||
|
||||
const startupSh = `#!/bin/sh
|
||||
# At container start this script writes /etc/libretime/config.yml from env
|
||||
cat > /etc/libretime/config.yml <<EOF
|
||||
general:
|
||||
public_url: ${publicUrl}
|
||||
api_key: "${values.api_key || ''}"
|
||||
secret_key: "${values.secret_key || ''}"
|
||||
|
||||
database:
|
||||
host: ${values.database_host || 'postgres'}
|
||||
port: 5432
|
||||
name: libretime
|
||||
user: libretime
|
||||
password: "$POSTGRES_PASSWORD"
|
||||
|
||||
rabbitmq:
|
||||
host: ${values.rabbitmq_host || 'rabbitmq'}
|
||||
port: 5672
|
||||
vhost: /libretime
|
||||
user: libretime
|
||||
password: "$RABBITMQ_DEFAULT_PASS"
|
||||
EOF
|
||||
`;
|
||||
|
||||
return {
|
||||
files: [
|
||||
{ path: 'docker-compose.yml', content: compose },
|
||||
{ path: 'startup/generate-config.sh', content: startupSh },
|
||||
{ path: 'docker/nginx/default.conf', content: require('fs').readFileSync('docker/nginx/default.conf','utf8') },
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default t;
|
||||
@ -1,28 +0,0 @@
|
||||
name: libretime-secure
|
||||
title: LibreTime (EasyPanel) - Secure
|
||||
description: "Plantilla segura que evita archivos con secretos en claro: genera el config desde variables/secretos del panel mediante un servicio generador."
|
||||
version: "1.0.0"
|
||||
author: "Tu nombre"
|
||||
logo: logo.png
|
||||
screenshot: screenshot.png
|
||||
tags:
|
||||
- media
|
||||
- radio
|
||||
- docker
|
||||
- libretime
|
||||
|
||||
form:
|
||||
- id: postgres_password
|
||||
type: password
|
||||
title: Contraseña Postgres
|
||||
- id: rabbitmq_password
|
||||
type: password
|
||||
title: Contraseña RabbitMQ
|
||||
- id: public_url
|
||||
type: string
|
||||
title: URL pública
|
||||
default: "http://{{hostname}}:8080"
|
||||
- id: libretime_version
|
||||
type: string
|
||||
title: LibreTime image version
|
||||
default: "4.5"
|
||||
@ -1,15 +0,0 @@
|
||||
Plantilla EasyPanel para LibreTime
|
||||
|
||||
Pasos rápidos:
|
||||
|
||||
1) Copia la carpeta `easypanel-template-libretime` a un repositorio nuevo o en tu instancia de EasyPanel.
|
||||
2) Si usas el Playground de EasyPanel (`npm run dev` en https://github.com/easypanel-io/templates), duplica una plantilla existente y ajusta `meta.yaml` e `index.ts`.
|
||||
3) Desde el panel EasyPanel puedes crear una plantilla usando el JSON generado por `index.ts` o subiendo los archivos.
|
||||
|
||||
Cómo instalar un template personalizado en EasyPanel (resumen):
|
||||
- En la UI de EasyPanel ve a "Templates" -> "Create from JSON" y pega el JSON del template (o sube el ZIP con los archivos generados).
|
||||
- Alternativamente, añade la carpeta al repositorio oficial de templates y abre un Pull Request siguiendo las pautas en https://github.com/easypanel-io/templates.
|
||||
|
||||
Notas de seguridad:
|
||||
- Sustituye contraseñas por secretos gestionados o variables del entorno en producción.
|
||||
- No uses `latest` en imágenes en producción; fija versiones.
|
||||
@ -1,151 +0,0 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-libretime}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-libretime}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-libretime}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-libretime}
|
||||
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
volumes:
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- legacy
|
||||
- api
|
||||
ports:
|
||||
- "${NGINX_PORT:-8080}:8080"
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
environment:
|
||||
- NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-auto}
|
||||
|
||||
icecast:
|
||||
image: ghcr.io/libretime/icecast:2.4.4
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${ICECAST_PORT:-8000}:8000"
|
||||
environment:
|
||||
ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-hackme}
|
||||
ICECAST_RELAY_PASSWORD: ${ICECAST_RELAY_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_USER: ${ICECAST_ADMIN_USER:-admin}
|
||||
ICECAST_HOSTNAME: ${ICECAST_HOSTNAME:-localhost}
|
||||
|
||||
playout:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
liquidsoap:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
command: /usr/local/bin/libretime-liquidsoap
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "${LIQUIDSOAP_HARBOR_PORT:-8001}:8001"
|
||||
- "${LIQUIDSOAP_TELNET_PORT:-8002}:8002"
|
||||
volumes:
|
||||
- ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
worker:
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./config.local.yml:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
analyzer:
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./config.local.yml:/etc/libretime/config.yml:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
libretime_storage:
|
||||
driver: local
|
||||
libretime_playout:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: libretime_network
|
||||
@ -1,72 +0,0 @@
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
|
||||
root /var/www/html/public;
|
||||
|
||||
index index.php index.html index.htm;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_buffers 64 4K;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
try_files $fastcgi_script_name =404;
|
||||
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
set $path_info $fastcgi_path_info;
|
||||
fastcgi_param PATH_INFO $path_info;
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_index index.php;
|
||||
fastcgi_pass legacy:9000;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ^~ /api/_media {
|
||||
internal;
|
||||
alias /srv/libretime;
|
||||
}
|
||||
|
||||
location ^~ /api/version {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
location ^~ /api/version/ {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
location = /api/register-component {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location = /api/register-component/ {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
|
||||
location ~ ^/api/(v2|browser) {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
server {
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
|
||||
root /var/www/html/public;
|
||||
|
||||
index index.php index.html index.htm;
|
||||
|
||||
client_max_body_size 512M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_buffers 64 4K;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
#try_files $uri =404;
|
||||
try_files $fastcgi_script_name =404;
|
||||
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
set $path_info $fastcgi_path_info;
|
||||
fastcgi_param PATH_INFO $path_info;
|
||||
include fastcgi_params;
|
||||
|
||||
fastcgi_index index.php;
|
||||
fastcgi_pass legacy:9000;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ^~ /api/_media {
|
||||
internal;
|
||||
alias /srv/libretime;
|
||||
}
|
||||
|
||||
location ^~ /api/version {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
location ^~ /api/version/ {
|
||||
return 307 /api/v2/version;
|
||||
}
|
||||
|
||||
location = /api/register-component {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location = /api/register-component/ {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
|
||||
location ~ ^/api/(v2|browser) {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api:9001;
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
// Minimal EasyPanel template generator that emits the canonical docker-compose
|
||||
// and an .env file. This follows the standard LibreTime Docker deployment and
|
||||
// does not add any extra services (e.g. composer).
|
||||
|
||||
import { Template } from '@easypanel/template-sdk';
|
||||
const fs = require('fs');
|
||||
|
||||
const t: Template = {
|
||||
meta: require('./meta.yaml'),
|
||||
render: (values: any) => {
|
||||
const composePath = './docker-compose.yml';
|
||||
let composeContent = '';
|
||||
try {
|
||||
composeContent = fs.readFileSync(composePath, 'utf8');
|
||||
} catch (e) {
|
||||
composeContent = '# docker-compose source not found: ' + composePath + '\n';
|
||||
}
|
||||
|
||||
const env = `POSTGRES_PASSWORD=${values.postgres_password || ''}\nRABBITMQ_DEFAULT_PASS=${values.rabbitmq_password || ''}\nLIBRETIME_GENERAL_PUBLIC_URL=${values.public_url || 'http://{{hostname}}:8080'}\n`;
|
||||
|
||||
let nginxConf = '';
|
||||
try {
|
||||
nginxConf = fs.readFileSync('./docker/nginx/default.conf', 'utf8');
|
||||
} catch (e) {
|
||||
nginxConf = '';
|
||||
}
|
||||
|
||||
return {
|
||||
files: [
|
||||
{ path: 'docker-compose.yml', content: composeContent },
|
||||
{ path: '.env', content: env },
|
||||
{ path: 'docker/nginx/default.conf', content: nginxConf },
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default t;
|
||||
@ -1 +0,0 @@
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAUA
|
||||
@ -1,2 +0,0 @@
|
||||
// meta.ts is generated by the template tooling from meta.yaml. It's included as a placeholder.
|
||||
export default require('./meta.yaml');
|
||||
@ -1,30 +0,0 @@
|
||||
name: libretime
|
||||
title: LibreTime (EasyPanel)
|
||||
description: "Plantilla para desplegar LibreTime usando docker-compose adaptado a EasyPanel. Incluye variables para credenciales y el archivo de configuración montado."
|
||||
version: "1.0.0"
|
||||
author: "Tu nombre"
|
||||
logo: logo.png
|
||||
screenshot: screenshot.png
|
||||
tags:
|
||||
- media
|
||||
- radio
|
||||
- docker
|
||||
- libretime
|
||||
|
||||
# Formulario que EasyPanel mostrará al crear una instancia desde la plantilla.
|
||||
form:
|
||||
- id: postgres_password
|
||||
type: password
|
||||
title: Contraseña Postgres
|
||||
default: "P@5sW0rd!8xYv7Q2z9L"
|
||||
- id: rabbitmq_password
|
||||
type: password
|
||||
title: Contraseña RabbitMQ
|
||||
default: "R@bbiTmq!2025xYz"
|
||||
- id: public_url
|
||||
type: string
|
||||
title: URL pública
|
||||
default: "http://{{hostname}}:8080"
|
||||
|
||||
# Nota: este archivo es solo metadatos y ejemplo. El motor de plantillas de Easypanel
|
||||
# usa además un archivo TypeScript (`index.ts`) para generar artefactos dinámicos.
|
||||
@ -1 +0,0 @@
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAUA
|
||||
@ -1,25 +0,0 @@
|
||||
{
|
||||
"meta": "name: libretime-secure\ntitle: LibreTime (EasyPanel) - Secure\ndescription: \"Plantilla segura que evita archivos con secretos en claro: genera el config desde variables/secretos del panel mediante un servicio generador.\"\nversion: \"1.0.0\"\nauthor: \"Tu nombre\"\nlogo: logo.png\nscreenshot: screenshot.png\ntags:\n - media\n - radio\n - docker\n - libretime\n\nform:\n - id: postgres_password\n type: password\n title: Contraseña Postgres\n - id: rabbitmq_password\n type: password\n title: Contraseña RabbitMQ\n - id: public_url\n type: string\n title: URL pública\n default: \"http://{{hostname}}:8080\"\n - id: libretime_version\n type: string\n title: LibreTime image version\n default: \"4.5\"\n",
|
||||
"files": [
|
||||
{
|
||||
"path": "docker/nginx/default.conf",
|
||||
"encoding": "utf8",
|
||||
"content": "server {\n listen 8080;\n listen [::]:8080;\n\n root /var/www/html/public;\n\n index index.php index.html index.htm;\n\n client_max_body_size 512M;\n client_body_timeout 300s;\n\n location ~ \\.php$ {\n fastcgi_buffers 64 4K;\n fastcgi_split_path_info ^(.+\\.php)(/.+)$;\n\n try_files $fastcgi_script_name =404;\n\n include fastcgi_params;\n\n fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n set $path_info $fastcgi_path_info;\n fastcgi_param PATH_INFO $path_info;\n include fastcgi_params;\n\n fastcgi_index index.php;\n fastcgi_pass legacy:9000;\n }\n\n location / {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location ^~ /api/_media {\n internal;\n alias /srv/libretime;\n }\n\n location ^~ /api/version {\n return 307 /api/v2/version;\n }\n\n location ^~ /api/version/ {\n return 307 /api/v2/version;\n }\n\n location = /api/register-component {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location = /api/register-component/ {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location /api/ {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_redirect off;\n proxy_pass http://api:9001;\n }\n\n location ~ ^/api/(v2|browser) {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n\n proxy_redirect off;\n proxy_pass http://api:9001;\n }\n}\n"
|
||||
},
|
||||
{
|
||||
"path": "docker-compose.yml",
|
||||
"encoding": "utf8",
|
||||
"content": "version: '3.8'\nservices:\n postgres:\n image: postgres:15\n environment:\n POSTGRES_USER: libretime\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n POSTGRES_DB: libretime\n volumes:\n - postgres_data:/var/lib/postgresql/data\n\n rabbitmq:\n image: rabbitmq:3.13-alpine\n environment:\n RABBITMQ_DEFAULT_VHOST: /libretime\n RABBITMQ_DEFAULT_USER: libretime\n RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}\n\n api:\n image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}\n environment:\n - LIBRETIME_GENERAL_PUBLIC_URL=${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}\n - LIBRETIME_CONFIG_FROM_ENV=true\n volumes:\n - libretime_storage:/srv/libretime\n depends_on:\n - postgres\n - rabbitmq\n\n legacy:\n image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}\n environment:\n - LIBRETIME_CONFIG_FROM_ENV=true\n volumes:\n - libretime_storage:/srv/libretime\n depends_on:\n - postgres\n - rabbitmq\n\n nginx:\n image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}\n ports:\n - \"8080:8080\"\n volumes:\n - libretime_storage:/srv/libretime:ro\n - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro\n depends_on:\n - legacy\n - api\n\nvolumes:\n postgres_data:\n libretime_storage:\n"
|
||||
},
|
||||
{
|
||||
"path": "index.ts",
|
||||
"encoding": "utf8",
|
||||
"content": "import { Template } from '@easypanel/template-sdk';\n\n// This secure template generates the docker-compose and uses environment variables\n// so that secrets are not stored in files inside the project. Instead, config\n// is generated at container start from env vars injected by the panel.\n\nconst t: Template = {\n meta: require('./meta.yaml'),\n render: (values: any) => {\n const libVersion = values.libretime_version || '4.5';\n const publicUrl = values.public_url || 'http://{{hostname}}:8080';\n\n const compose = `version: '3.8'\nservices:\n postgres:\n image: postgres:15\n environment:\n POSTGRES_USER: libretime\n POSTGRES_PASSWORD: ${values.postgres_password ? `'${values.postgres_password}'` : \"\\\"\\\"\"}\n POSTGRES_DB: libretime\n volumes:\n - postgres_data:/var/lib/postgresql/data\n\n rabbitmq:\n image: rabbitmq:3.13-alpine\n environment:\n RABBITMQ_DEFAULT_VHOST: /libretime\n RABBITMQ_DEFAULT_USER: libretime\n RABBITMQ_DEFAULT_PASS: ${values.rabbitmq_password ? `'${values.rabbitmq_password}'` : \"\\\"\\\"\"}\n\n api:\n image: ghcr.io/libretime/libretime-api:${libVersion}\n environment:\n - LIBRETIME_GENERAL_PUBLIC_URL=${publicUrl}\n - LIBRETIME_CONFIG_FROM_ENV=true\n volumes:\n - libretime_storage:/srv/libretime\n depends_on:\n - postgres\n - rabbitmq\n\n # composer not included; follow standard LibreTime docker installation\n\n legacy:\n image: ghcr.io/libretime/libretime-legacy:${libVersion}\n environment:\n - LIBRETIME_CONFIG_FROM_ENV=true\n volumes:\n - libretime_storage:/srv/libretime\n depends_on:\n - postgres\n - rabbitmq\n\n nginx:\n image: ghcr.io/libretime/libretime-nginx:${libVersion}\n ports:\n - \"8080:8080\"\n volumes:\n - libretime_storage:/srv/libretime:ro\n - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro\n depends_on:\n - legacy\n - api\n\nvolumes:\n postgres_data:\n libretime_storage:\n`;\n\n const startupSh = `#!/bin/sh\n# At container start this script writes /etc/libretime/config.yml from env\ncat > /etc/libretime/config.yml <<EOF\ngeneral:\n public_url: ${publicUrl}\n api_key: \"${values.api_key || ''}\"\n secret_key: \"${values.secret_key || ''}\"\n\ndatabase:\n host: ${values.database_host || 'postgres'}\n port: 5432\n name: libretime\n user: libretime\n password: \"$POSTGRES_PASSWORD\"\n\nrabbitmq:\n host: ${values.rabbitmq_host || 'rabbitmq'}\n port: 5672\n vhost: /libretime\n user: libretime\n password: \"$RABBITMQ_DEFAULT_PASS\"\nEOF\n`;\n\n return {\n files: [\n { path: 'docker-compose.yml', content: compose },\n { path: 'startup/generate-config.sh', content: startupSh },\n { path: 'docker/nginx/default.conf', content: require('fs').readFileSync('docker/nginx/default.conf','utf8') },\n ],\n };\n },\n};\n\nexport default t;\n"
|
||||
},
|
||||
{
|
||||
"path": "meta.yaml",
|
||||
"encoding": "utf8",
|
||||
"content": "name: libretime-secure\ntitle: LibreTime (EasyPanel) - Secure\ndescription: \"Plantilla segura que evita archivos con secretos en claro: genera el config desde variables/secretos del panel mediante un servicio generador.\"\nversion: \"1.0.0\"\nauthor: \"Tu nombre\"\nlogo: logo.png\nscreenshot: screenshot.png\ntags:\n - media\n - radio\n - docker\n - libretime\n\nform:\n - id: postgres_password\n type: password\n title: Contraseña Postgres\n - id: rabbitmq_password\n type: password\n title: Contraseña RabbitMQ\n - id: public_url\n type: string\n title: URL pública\n default: \"http://{{hostname}}:8080\"\n - id: libretime_version\n type: string\n title: LibreTime image version\n default: \"4.5\"\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
{
|
||||
"meta": "name: libretime\ntitle: LibreTime (EasyPanel)\ndescription: \"Plantilla para desplegar LibreTime usando docker-compose adaptado a EasyPanel. Incluye variables para credenciales y el archivo de configuración montado.\"\nversion: \"1.0.0\"\nauthor: \"Tu nombre\"\nlogo: logo.png\nscreenshot: screenshot.png\ntags:\n - media\n - radio\n - docker\n - libretime\n\n# Formulario que EasyPanel mostrará al crear una instancia desde la plantilla.\nform:\n - id: postgres_password\n type: password\n title: Contraseña Postgres\n default: \"P@5sW0rd!8xYv7Q2z9L\"\n - id: rabbitmq_password\n type: password\n title: Contraseña RabbitMQ\n default: \"R@bbiTmq!2025xYz\"\n - id: public_url\n type: string\n title: URL pública\n default: \"http://{{hostname}}:8080\"\n\n# Nota: este archivo es solo metadatos y ejemplo. El motor de plantillas de Easypanel\n# usa además un archivo TypeScript (`index.ts`) para generar artefactos dinámicos.\n",
|
||||
"files": [
|
||||
{
|
||||
"path": "README.easypanel.md",
|
||||
"encoding": "utf8",
|
||||
"content": "Plantilla EasyPanel para LibreTime\n\nPasos rápidos:\n\n1) Copia la carpeta `easypanel-template-libretime` a un repositorio nuevo o en tu instancia de EasyPanel.\n2) Si usas el Playground de EasyPanel (`npm run dev` en https://github.com/easypanel-io/templates), duplica una plantilla existente y ajusta `meta.yaml` e `index.ts`.\n3) Desde el panel EasyPanel puedes crear una plantilla usando el JSON generado por `index.ts` o subiendo los archivos.\n\nCómo instalar un template personalizado en EasyPanel (resumen):\n- En la UI de EasyPanel ve a \"Templates\" -> \"Create from JSON\" y pega el JSON del template (o sube el ZIP con los archivos generados).\n- Alternativamente, añade la carpeta al repositorio oficial de templates y abre un Pull Request siguiendo las pautas en https://github.com/easypanel-io/templates.\n\nNotas de seguridad:\n- Sustituye contraseñas por secretos gestionados o variables del entorno en producción.\n- No uses `latest` en imágenes en producción; fija versiones.\n"
|
||||
},
|
||||
{
|
||||
"path": "docker/nginx/default.conf",
|
||||
"encoding": "utf8",
|
||||
"content": "server {\n listen 8080;\n listen [::]:8080;\n\n root /var/www/html/public;\n\n index index.php index.html index.htm;\n\n client_max_body_size 512M;\n client_body_timeout 300s;\n\n location ~ \\.php$ {\n fastcgi_buffers 64 4K;\n fastcgi_split_path_info ^(.+\\.php)(/.+)$;\n\n try_files $fastcgi_script_name =404;\n\n include fastcgi_params;\n\n fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n set $path_info $fastcgi_path_info;\n fastcgi_param PATH_INFO $path_info;\n include fastcgi_params;\n\n fastcgi_index index.php;\n fastcgi_pass legacy:9000;\n }\n\n location / {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location ^~ /api/_media {\n internal;\n alias /srv/libretime;\n }\n\n location ^~ /api/version {\n return 307 /api/v2/version;\n }\n\n location ^~ /api/version/ {\n return 307 /api/v2/version;\n }\n\n location = /api/register-component {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location = /api/register-component/ {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location /api/ {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_redirect off;\n proxy_pass http://api:9001;\n }\n\n location ~ ^/api/(v2|browser) {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n\n proxy_redirect off;\n proxy_pass http://api:9001;\n }\n}\n"
|
||||
},
|
||||
{
|
||||
"path": "docker-compose.yml",
|
||||
"encoding": "utf8",
|
||||
"content": "version: '3.8'\nservices:\n postgres:\n image: postgres:15\n restart: unless-stopped\n environment:\n POSTGRES_USER: ${POSTGRES_USER:-libretime}\n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-libretime}\n POSTGRES_DB: ${POSTGRES_DB:-libretime}\n volumes:\n - postgres_data:/var/lib/postgresql/data\n\n rabbitmq:\n image: rabbitmq:3.13-alpine\n restart: unless-stopped\n environment:\n RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}\n RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}\n RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-libretime}\n\n api:\n image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}\n restart: unless-stopped\n init: true\n ulimits:\n nofile: 1024\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n environment:\n LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}\n volumes:\n - ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro\n - libretime_storage:/srv/libretime\n\n legacy:\n image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}\n restart: unless-stopped\n init: true\n ulimits:\n nofile: 1024\n depends_on:\n postgres:\n condition: service_healthy\n rabbitmq:\n condition: service_healthy\n volumes:\n - ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro\n - libretime_storage:/srv/libretime\n\n nginx:\n image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}\n restart: unless-stopped\n depends_on:\n - legacy\n - api\n ports:\n - \"${NGINX_PORT:-8080}:8080\"\n volumes:\n - libretime_storage:/srv/libretime:ro\n - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro\n environment:\n - NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-auto}\n\n icecast:\n image: ghcr.io/libretime/icecast:2.4.4\n restart: unless-stopped\n ports:\n - \"${ICECAST_PORT:-8000}:8000\"\n environment:\n ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}\n ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-hackme}\n ICECAST_RELAY_PASSWORD: ${ICECAST_RELAY_PASSWORD:-hackme}\n ICECAST_ADMIN_USER: ${ICECAST_ADMIN_USER:-admin}\n ICECAST_HOSTNAME: ${ICECAST_HOSTNAME:-localhost}\n\n playout:\n image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}\n restart: unless-stopped\n init: true\n ulimits:\n nofile: 1024\n depends_on:\n rabbitmq:\n condition: service_healthy\n volumes:\n - ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro\n - libretime_playout:/app\n environment:\n LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}\n\n liquidsoap:\n image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}\n command: /usr/local/bin/libretime-liquidsoap\n restart: unless-stopped\n init: true\n ulimits:\n nofile: 1024\n depends_on:\n rabbitmq:\n condition: service_healthy\n ports:\n - \"${LIQUIDSOAP_HARBOR_PORT:-8001}:8001\"\n - \"${LIQUIDSOAP_TELNET_PORT:-8002}:8002\"\n volumes:\n - ${LIBRETIME_CONFIG_FILEPATH:-./config.local.yml}:/etc/libretime/config.yml:ro\n - libretime_playout:/app\n environment:\n LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}\n\n worker:\n image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-4.5}\n restart: unless-stopped\n init: true\n ulimits:\n nofile: 1024\n depends_on:\n rabbitmq:\n condition: service_healthy\n volumes:\n - ./config.local.yml:/etc/libretime/config.yml:ro\n - libretime_storage:/srv/libretime\n environment:\n LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}\n\n analyzer:\n image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-4.5}\n restart: unless-stopped\n init: true\n ulimits:\n nofile: 1024\n depends_on:\n rabbitmq:\n condition: service_healthy\n volumes:\n - ./config.local.yml:/etc/libretime/config.yml:ro\n - libretime_storage:/srv/libretime\n\nvolumes:\n postgres_data:\n driver: local\n libretime_storage:\n driver: local\n libretime_playout:\n driver: local\n\nnetworks:\n default:\n name: libretime_network\n"
|
||||
},
|
||||
{
|
||||
"path": "docker_nginx_default.conf",
|
||||
"encoding": "utf8",
|
||||
"content": "server {\n listen 8080;\n listen [::]:8080;\n\n root /var/www/html/public;\n\n index index.php index.html index.htm;\n\n client_max_body_size 512M;\n client_body_timeout 300s;\n\n location ~ \\.php$ {\n fastcgi_buffers 64 4K;\n fastcgi_split_path_info ^(.+\\.php)(/.+)$;\n\n #try_files $uri =404;\n try_files $fastcgi_script_name =404;\n\n include fastcgi_params;\n\n fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;\n set $path_info $fastcgi_path_info;\n fastcgi_param PATH_INFO $path_info;\n include fastcgi_params;\n\n fastcgi_index index.php;\n fastcgi_pass legacy:9000;\n }\n\n location / {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location ^~ /api/_media {\n internal;\n alias /srv/libretime;\n }\n\n location ^~ /api/version {\n return 307 /api/v2/version;\n }\n\n location ^~ /api/version/ {\n return 307 /api/v2/version;\n }\n\n location = /api/register-component {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location = /api/register-component/ {\n try_files $uri $uri/ /index.php$is_args$args;\n }\n\n location /api/ {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_redirect off;\n proxy_pass http://api:9001;\n }\n\n location ~ ^/api/(v2|browser) {\n proxy_set_header Host $http_host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n\n proxy_redirect off;\n proxy_pass http://api:9001;\n }\n}\n"
|
||||
},
|
||||
{
|
||||
"path": "index.ts",
|
||||
"encoding": "utf8",
|
||||
"content": "// Minimal EasyPanel template generator that emits the canonical docker-compose\n// and an .env file. This follows the standard LibreTime Docker deployment and\n// does not add any extra services (e.g. composer).\n\nimport { Template } from '@easypanel/template-sdk';\nconst fs = require('fs');\n\nconst t: Template = {\n meta: require('./meta.yaml'),\n render: (values: any) => {\n const composePath = './docker-compose.yml';\n let composeContent = '';\n try {\n composeContent = fs.readFileSync(composePath, 'utf8');\n } catch (e) {\n composeContent = '# docker-compose source not found: ' + composePath + '\\n';\n }\n\n const env = `POSTGRES_PASSWORD=${values.postgres_password || ''}\\nRABBITMQ_DEFAULT_PASS=${values.rabbitmq_password || ''}\\nLIBRETIME_GENERAL_PUBLIC_URL=${values.public_url || 'http://{{hostname}}:8080'}\\n`;\n\n let nginxConf = '';\n try {\n nginxConf = fs.readFileSync('./docker/nginx/default.conf', 'utf8');\n } catch (e) {\n nginxConf = '';\n }\n\n return {\n files: [\n { path: 'docker-compose.yml', content: composeContent },\n { path: '.env', content: env },\n { path: 'docker/nginx/default.conf', content: nginxConf },\n ],\n };\n },\n};\n\nexport default t;\n"
|
||||
},
|
||||
{
|
||||
"path": "logo.png",
|
||||
"encoding": "utf8",
|
||||
"content": "iVBORw0KGgoAAAANSUhEUgAAAAUA\n"
|
||||
},
|
||||
{
|
||||
"path": "meta.ts",
|
||||
"encoding": "utf8",
|
||||
"content": "// meta.ts is generated by the template tooling from meta.yaml. It's included as a placeholder.\nexport default require('./meta.yaml');\n"
|
||||
},
|
||||
{
|
||||
"path": "meta.yaml",
|
||||
"encoding": "utf8",
|
||||
"content": "name: libretime\ntitle: LibreTime (EasyPanel)\ndescription: \"Plantilla para desplegar LibreTime usando docker-compose adaptado a EasyPanel. Incluye variables para credenciales y el archivo de configuración montado.\"\nversion: \"1.0.0\"\nauthor: \"Tu nombre\"\nlogo: logo.png\nscreenshot: screenshot.png\ntags:\n - media\n - radio\n - docker\n - libretime\n\n# Formulario que EasyPanel mostrará al crear una instancia desde la plantilla.\nform:\n - id: postgres_password\n type: password\n title: Contraseña Postgres\n default: \"P@5sW0rd!8xYv7Q2z9L\"\n - id: rabbitmq_password\n type: password\n title: Contraseña RabbitMQ\n default: \"R@bbiTmq!2025xYz\"\n - id: public_url\n type: string\n title: URL pública\n default: \"http://{{hostname}}:8080\"\n\n# Nota: este archivo es solo metadatos y ejemplo. El motor de plantillas de Easypanel\n# usa además un archivo TypeScript (`index.ts`) para generar artefactos dinámicos.\n"
|
||||
},
|
||||
{
|
||||
"path": "screenshot.png",
|
||||
"encoding": "utf8",
|
||||
"content": "iVBORw0KGgoAAAANSUhEUgAAAAUA\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
# LibreTime (EasyPanel)
|
||||
|
||||
Este directorio contiene metadatos y un script de actualización en el estilo que usa EasyPanel para instalar un servicio desde un repositorio.
|
||||
|
||||
Qué hace:
|
||||
- `update.sh` copia `docker-compose.easypanel.yml` a `./code/docker-compose.yml` y elimina `container_name` y `ports` para que el `docker-compose` resultante sea compatible con EasyPanel.
|
||||
|
||||
Cómo funciona (resumen):
|
||||
- EasyPanel suele clonar el repo en una carpeta temporal, ejecutar `update.sh` y luego usar `./code/docker-compose.yml` como el `compose` a ejecutar.
|
||||
|
||||
Requisitos/Notas:
|
||||
- `update.sh` usa utilidades estándar de shell y `perl` (normalmente disponible en sistemas UNIX). Si tu entorno no tiene `perl`, el script puede adaptarse a `python`.
|
||||
- Revisa y completa las variables de entorno requeridas por `docker-compose.easypanel.yml` (p. ej. secretos: `POSTGRES_PASSWORD`, `RABBITMQ_DEFAULT_PASS`, etc.) desde la UI de EasyPanel.
|
||||
|
||||
Siguientes pasos recomendados:
|
||||
- (Opcional) Añadir un `update.js` si prefieres implementar clonación/actualizaciones con las utilidades de EasyPanel (no obligatorio).
|
||||
- (Opcional) Añadir un `update.js` si prefieres implementar clonación/actualizaciones con las utilidades de EasyPanel (no obligatorio). Este repo incluye `easypanel/update.js` que realiza la misma función que `update.sh` usando Node.js.
|
||||
- (Opcional) Añadir un archivo `README.md` con la lista de variables que EasyPanel debe exponer en la interfaz de instalación.
|
||||
|
||||
@ -1,199 +0,0 @@
|
||||
version: "3.9"
|
||||
|
||||
# LibreTime Docker Compose (EasyPanel-ready)
|
||||
# Esta versión está pensada para usarse desde un repositorio en EasyPanel.
|
||||
# Principios:
|
||||
# - No almacenar secretos en archivos del repo: EasyPanel proporcionará los valores
|
||||
# mediante variables/secretos de entorno.
|
||||
# - Un servicio `config-generator` genera `/config/config.yml` dentro del volumen
|
||||
# `libretime_config` a partir de variables de entorno. Los servicios montan ese
|
||||
# volumen en `/etc/libretime`.
|
||||
|
||||
services:
|
||||
# Base de datos PostgreSQL
|
||||
postgres:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-libretime}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-libretime}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-libretime}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-libretime}"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# Message broker RabbitMQ
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.13-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "rabbitmq-diagnostics check_port_connectivity"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# API de LibreTime
|
||||
api:
|
||||
image: ghcr.io/libretime/libretime-api:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
config-generator:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
LIBRETIME_DEBUG: ${LIBRETIME_DEBUG:-false}
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# Aplicación Legacy de LibreTime
|
||||
legacy:
|
||||
image: ghcr.io/libretime/libretime-legacy:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
config-generator:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# Servidor web Nginx
|
||||
nginx:
|
||||
image: ghcr.io/libretime/libretime-nginx:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- legacy
|
||||
- api
|
||||
volumes:
|
||||
- libretime_storage:/srv/libretime:ro
|
||||
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
environment:
|
||||
- NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-auto}
|
||||
|
||||
# Servidor de streaming Icecast
|
||||
icecast:
|
||||
image: ghcr.io/libretime/icecast:2.4.4
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-hackme}
|
||||
ICECAST_RELAY_PASSWORD: ${ICECAST_RELAY_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_USER: ${ICECAST_ADMIN_USER:-admin}
|
||||
ICECAST_HOSTNAME: ${ICECAST_HOSTNAME:-localhost}
|
||||
|
||||
# Servicio de playout
|
||||
playout:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
# Liquidsoap para streaming
|
||||
liquidsoap:
|
||||
image: ghcr.io/libretime/libretime-playout:${LIBRETIME_VERSION:-4.5}
|
||||
command: /usr/local/bin/libretime-liquidsoap
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_playout:/app
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
# Worker para tareas en background
|
||||
worker:
|
||||
image: ghcr.io/libretime/libretime-worker:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
environment:
|
||||
LIBRETIME_GENERAL_PUBLIC_URL: ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}
|
||||
|
||||
# Analyzer para análisis de archivos de audio
|
||||
analyzer:
|
||||
image: ghcr.io/libretime/libretime-analyzer:${LIBRETIME_VERSION:-4.5}
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ulimits:
|
||||
nofile: 1024
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- libretime_config:/etc/libretime:ro
|
||||
- libretime_storage:/srv/libretime
|
||||
|
||||
# (No se incluye servicio composer; seguir método Docker estándar de LibreTime)
|
||||
|
||||
# Generador de configuración (escrito por el panel desde variables/secretos)
|
||||
config-generator:
|
||||
image: alpine:3.18
|
||||
restart: "no"
|
||||
entrypoint: ["/bin/sh","/tools/easypanel-config-generator.sh"]
|
||||
volumes:
|
||||
- libretime_config:/config
|
||||
- ./tools:/tools:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL","test -f /config/config.yml"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
# Volúmenes persistentes
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
libretime_storage:
|
||||
driver: local
|
||||
libretime_playout:
|
||||
driver: local
|
||||
libretime_config:
|
||||
driver: local
|
||||
|
||||
# Red personalizada (opcional para EasyPanel)
|
||||
networks:
|
||||
default:
|
||||
name: libretime_network
|
||||
@ -1,123 +0,0 @@
|
||||
{
|
||||
"name": "LibreTime",
|
||||
"slug": "libretime",
|
||||
"version": "1.0.0",
|
||||
"description": "LibreTime: sistema de automatización y streaming de radio. Servicio preparado para EasyPanel mediante docker-compose.",
|
||||
"author": "LibreTime contributors",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/your-org/your-repo"
|
||||
},
|
||||
"update_script": "easypanel/update.js",
|
||||
"compose_path": "easypanel/code/docker-compose.yml",
|
||||
"services": {
|
||||
"notes": "El archivo compose generado tendrá los servicios principales: postgres, rabbitmq, api, legacy, nginx, icecast, playout, liquidsoap, worker, analyzer y config-generator."
|
||||
},
|
||||
"env": [
|
||||
{
|
||||
"name": "POSTGRES_USER",
|
||||
"description": "Usuario de la base de datos Postgres",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "libretime"
|
||||
},
|
||||
{
|
||||
"name": "POSTGRES_PASSWORD",
|
||||
"description": "Contraseña de Postgres (requerida)",
|
||||
"required": true,
|
||||
"secret": true
|
||||
},
|
||||
{
|
||||
"name": "POSTGRES_DB",
|
||||
"description": "Nombre de la base de datos",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "libretime"
|
||||
},
|
||||
{
|
||||
"name": "RABBITMQ_DEFAULT_VHOST",
|
||||
"description": "VHost por defecto para RabbitMQ",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "/libretime"
|
||||
},
|
||||
{
|
||||
"name": "RABBITMQ_DEFAULT_USER",
|
||||
"description": "Usuario por defecto para RabbitMQ",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "libretime"
|
||||
},
|
||||
{
|
||||
"name": "RABBITMQ_DEFAULT_PASS",
|
||||
"description": "Contraseña de RabbitMQ (requerida)",
|
||||
"required": true,
|
||||
"secret": true
|
||||
},
|
||||
{
|
||||
"name": "LIBRETIME_VERSION",
|
||||
"description": "Versión de las imágenes de LibreTime",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "4.5"
|
||||
},
|
||||
{
|
||||
"name": "LIBRETIME_GENERAL_PUBLIC_URL",
|
||||
"description": "URL pública de la instalación",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "http://localhost:8080"
|
||||
},
|
||||
{
|
||||
"name": "LIBRETIME_DEBUG",
|
||||
"description": "Habilitar modo debug (true/false)",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"name": "NGINX_PORT",
|
||||
"description": "Puerto HTTP expuesto por Nginx (opcional si EasyPanel maneja el routing)",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "8080"
|
||||
},
|
||||
{
|
||||
"name": "ICECAST_PORT",
|
||||
"description": "Puerto de Icecast",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "8000"
|
||||
},
|
||||
{
|
||||
"name": "ICECAST_SOURCE_PASSWORD",
|
||||
"description": "Contraseña de source para Icecast",
|
||||
"required": false,
|
||||
"secret": true,
|
||||
"default": "hackme"
|
||||
},
|
||||
{
|
||||
"name": "ICECAST_ADMIN_PASSWORD",
|
||||
"description": "Contraseña de admin para Icecast",
|
||||
"required": false,
|
||||
"secret": true,
|
||||
"default": "hackme"
|
||||
},
|
||||
{
|
||||
"name": "ICECAST_RELAY_PASSWORD",
|
||||
"description": "Contraseña de relay para Icecast",
|
||||
"required": false,
|
||||
"secret": true,
|
||||
"default": "hackme"
|
||||
},
|
||||
{
|
||||
"name": "ICECAST_ADMIN_USER",
|
||||
"description": "Usuario admin Icecast",
|
||||
"required": false,
|
||||
"secret": false,
|
||||
"default": "admin"
|
||||
}
|
||||
],
|
||||
"notes": "Después de clonar, EasyPanel debe ejecutar el `update_script` y usar el compose generado en `compose_path`. Asegúrate de proveer los secretos marcados como `secret: true` en la UI de EasyPanel.",
|
||||
"last_updated": "2025-10-01"
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
// update.js - script de utilidad para EasyPanel
|
||||
// Copia docker-compose.easypanel.yml a ./code/docker-compose.yml y elimina
|
||||
// container_name y ports para que EasyPanel gestione nombres y puertos.
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SRC = path.resolve(__dirname, '../docker-compose.easypanel.yml');
|
||||
const DEST_DIR = path.resolve(__dirname, './code');
|
||||
const DEST = path.join(DEST_DIR, 'docker-compose.yml');
|
||||
|
||||
function removeContainerNamesAndPorts(content) {
|
||||
// Eliminar container_name: <valor> líneas
|
||||
content = content.replace(/^[ \t]*container_name:[^\n]*\n/gm, '');
|
||||
// Eliminar bloques de ports:
|
||||
// ports:\n - "..."\n - ...
|
||||
content = content.replace(/^[ \t]*ports:[^\n]*\n(?:^[ \t]+-.*\n)*/gim, '');
|
||||
return content;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!fs.existsSync(SRC)) {
|
||||
console.error('ERROR: no se encuentra', SRC);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(DEST_DIR)) fs.mkdirSync(DEST_DIR, { recursive: true });
|
||||
|
||||
const raw = fs.readFileSync(SRC, 'utf8');
|
||||
const sanitized = removeContainerNamesAndPorts(raw);
|
||||
|
||||
fs.writeFileSync(DEST, sanitized, 'utf8');
|
||||
console.log('Preparado', DEST);
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Este script prepara el compose para EasyPanel.
|
||||
# Si Node.js está disponible y existe easypanel/update.js, lo ejecuta.
|
||||
# De lo contrario, realiza la copia y saneado en bash/perl (fallback).
|
||||
|
||||
BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SRC="$BASE_DIR/../docker-compose.easypanel.yml"
|
||||
DEST_DIR="$BASE_DIR/code"
|
||||
DEST="$DEST_DIR/docker-compose.yml"
|
||||
|
||||
if command -v node >/dev/null 2>&1 && [ -f "$BASE_DIR/update.js" ]; then
|
||||
echo "Node.js detectado, ejecutando easypanel/update.js"
|
||||
# Ejecutar con node (permitirá usar la versión JS en lugar del sed/perl)
|
||||
node "$BASE_DIR/update.js"
|
||||
echo "Preparado $DEST para EasyPanel (vía update.js)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Node.js no disponible o easypanel/update.js no encontrado; usando fallback shell copy"
|
||||
|
||||
mkdir -p "$DEST_DIR"
|
||||
|
||||
if [ ! -f "$SRC" ]; then
|
||||
echo "ERROR: no se encuentra $SRC"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Copiar
|
||||
cp "$SRC" "$DEST"
|
||||
|
||||
# Eliminar container_name y ports keys (simplemente eliminamos las líneas que contienen 'container_name:' o 'ports:')
|
||||
# Esto es similar a lo que hacen muchos ejemplos de EasyPanel.
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
perl -0777 -pe "s/^[ \t]*container_name:[^\n]*\n//mg" -i "$DEST"
|
||||
perl -0777 -pe "s/^[ \t]*ports:[^\n]*\n(?:^[ \t]+-.*\n)*//mg" -i "$DEST"
|
||||
else
|
||||
# Fallback con awk/sed si perl no está disponible
|
||||
# Elimina líneas 'container_name:' y bloques 'ports:' simples
|
||||
sed -E '/^[ \t]*container_name:/d' -i "" "$DEST" 2>/dev/null || sed -E '/^[ \t]*container_name:/d' -i '$DEST'
|
||||
awk 'BEGIN{skip=0} /^[ \t]*ports:[ \t]*$/ {skip=1; next} /^[ \t]*[^ \t]/ { if(skip){skip=0} } { if(!skip) print $0 }' "$DEST" > "$DEST.tmp" && mv "$DEST.tmp" "$DEST"
|
||||
fi
|
||||
|
||||
echo "Preparado $DEST para EasyPanel. Revisa variables de entorno en el README y súbelas en la UI de EasyPanel."
|
||||
@ -1,35 +0,0 @@
|
||||
# Variables de entorno / Secretos requeridos para EasyPanel
|
||||
|
||||
Lista de variables que el `docker-compose.easypanel.yml` usa y que deberías exponer desde la UI de EasyPanel (Secrets/Environment):
|
||||
|
||||
- POSTGRES_USER (opcional, default: libretime)
|
||||
- POSTGRES_PASSWORD (requerido)
|
||||
- POSTGRES_DB (opcional, default: libretime)
|
||||
|
||||
- RABBITMQ_DEFAULT_VHOST (opcional, default: /libretime)
|
||||
- RABBITMQ_DEFAULT_USER (opcional, default: libretime)
|
||||
- RABBITMQ_DEFAULT_PASS (requerido)
|
||||
|
||||
- LIBRETIME_VERSION (opcional, default: 4.5)
|
||||
- LIBRETIME_GENERAL_PUBLIC_URL (opcional, default: http://localhost:8080)
|
||||
- LIBRETIME_DEBUG (opcional, default: false)
|
||||
|
||||
- NGINX_PORT (opcional, default: 8080) <-- Nota: EasyPanel típicamente gestiona mapeos de puertos; puedes dejarlo o permitir que EasyPanel lo reescriba.
|
||||
- ICECAST_PORT (opcional, default: 8000)
|
||||
- LIQUIDSOAP_HARBOR_PORT (opcional, default: 8001)
|
||||
- LIQUIDSOAP_TELNET_PORT (opcional, default: 8002)
|
||||
|
||||
- ICECAST_SOURCE_PASSWORD (opcional, default: hackme)
|
||||
- ICECAST_ADMIN_PASSWORD (opcional, default: hackme)
|
||||
- ICECAST_RELAY_PASSWORD (opcional, default: hackme)
|
||||
- ICECAST_ADMIN_USER (opcional, default: admin)
|
||||
- ICECAST_HOSTNAME (opcional, default: localhost)
|
||||
|
||||
- NGINX_WORKER_PROCESSES (opcional, default: auto)
|
||||
|
||||
Notas:
|
||||
- Marca `POSTGRES_PASSWORD` y `RABBITMQ_DEFAULT_PASS` como secretos en EasyPanel.
|
||||
- El `config-generator` espera que estas variables existan para generar `/config/config.yml`.
|
||||
- Si quieres que EasyPanel maneje el mapeo de puertos, deja `ports` en el `docker-compose.easypanel.yml` y el `update.sh` los eliminará cuando prepare `./code/docker-compose.yml` para la plataforma (esto sigue el patrón del repositorio oficial de EasyPanel).
|
||||
|
||||
Sugerencia: revisa los valores por defecto y decide qué variables quieres obligatorias en la UI de instalación.
|
||||
@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script para generar claves seguras para LibreTime
|
||||
# Uso: ./generate-keys.sh
|
||||
|
||||
echo "Generando claves seguras para LibreTime..."
|
||||
echo ""
|
||||
|
||||
# Generar API key (64 caracteres)
|
||||
API_KEY=$(openssl rand -hex 32)
|
||||
echo "API_KEY (copia esto a config.local.yml):"
|
||||
echo "$API_KEY"
|
||||
echo ""
|
||||
|
||||
# Generar Django secret key (50 caracteres)
|
||||
SECRET_KEY=$(openssl rand -base64 50 | tr -d "=+/" | cut -c1-50)
|
||||
echo "SECRET_KEY (copia esto a config.local.yml):"
|
||||
echo "$SECRET_KEY"
|
||||
echo ""
|
||||
|
||||
# Generar contraseñas para la base de datos
|
||||
DB_PASSWORD=$(openssl rand -base64 24 | tr -d "=+/" | cut -c1-20)
|
||||
echo "POSTGRES_PASSWORD (copia esto a .env):"
|
||||
echo "$DB_PASSWORD"
|
||||
echo ""
|
||||
|
||||
# Generar contraseña para RabbitMQ
|
||||
RABBITMQ_PASSWORD=$(openssl rand -base64 24 | tr -d "=+/" | cut -c1-20)
|
||||
echo "RABBITMQ_DEFAULT_PASS (copia esto a .env):"
|
||||
echo "$RABBITMQ_PASSWORD"
|
||||
echo ""
|
||||
|
||||
# Generar contraseñas para Icecast
|
||||
ICECAST_SOURCE=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-12)
|
||||
ICECAST_ADMIN=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-12)
|
||||
ICECAST_RELAY=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-12)
|
||||
|
||||
echo "ICECAST_SOURCE_PASSWORD (copia esto a .env):"
|
||||
echo "$ICECAST_SOURCE"
|
||||
echo ""
|
||||
|
||||
echo "ICECAST_ADMIN_PASSWORD (copia esto a .env):"
|
||||
echo "$ICECAST_ADMIN"
|
||||
echo ""
|
||||
|
||||
echo "ICECAST_RELAY_PASSWORD (copia esto a .env):"
|
||||
echo "$ICECAST_RELAY"
|
||||
echo ""
|
||||
|
||||
echo "==================================="
|
||||
echo "INSTRUCCIONES:"
|
||||
echo "1. Copia estas claves a tus archivos de configuración"
|
||||
echo "2. Actualiza config.local.yml con API_KEY y SECRET_KEY"
|
||||
echo "3. Actualiza .env con las contraseñas generadas"
|
||||
echo "4. También actualiza las contraseñas en config.local.yml para database y rabbitmq"
|
||||
echo "==================================="
|
||||
@ -1,19 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Helper para levantar LibreTime en macOS usando docker compose
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
if [ ! -f .env ]; then
|
||||
echo ".env not found — copying from .env.example"
|
||||
cp .env.example .env
|
||||
echo "Please edit .env and set secure passwords for POSTGRES_PASSWORD and RABBITMQ_DEFAULT_PASS, then re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Starting docker compose using docker-compose.easypanel.yml..."
|
||||
docker compose -f docker-compose.easypanel.yml up -d
|
||||
|
||||
echo "Done. To see logs: docker compose -f docker-compose.easypanel.yml logs -f api"
|
||||
@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Script helper: inspecciona el montaje de config y (opcionalmente) elimina y regenera
|
||||
# Uso:
|
||||
# bash scripts/fix-config-volume.sh # inspecciona y recrea config-generator
|
||||
# bash scripts/fix-config-volume.sh --force-delete # detiene stack, borra volumen libretime_config y regenera
|
||||
|
||||
ROOT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
echo "== comprobar que docker existe =="
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "ERROR: docker no encontrado en PATH. Instala/abre Docker Desktop y vuelve a intentarlo." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== docker compose ps =="
|
||||
docker compose -f docker-compose.easypanel.yml ps || true
|
||||
|
||||
CID=$(docker compose -f docker-compose.easypanel.yml ps -q api || true)
|
||||
echo "API CID: ${CID:-<no-cid>}"
|
||||
|
||||
if [ -n "${CID:-}" ]; then
|
||||
echo "== mounts del contenedor api =="
|
||||
docker inspect --format '{{json .Mounts}}' "$CID" | jq . || docker inspect --format '{{json .Mounts}}' "$CID" || true
|
||||
|
||||
echo "== contenido que ve la API en /etc/libretime =="
|
||||
docker compose -f docker-compose.easypanel.yml exec api sh -c 'ls -la /etc/libretime || true; sed -n "1,200p" /etc/libretime/config.yml || true' || true
|
||||
else
|
||||
echo "Contenedor API no está en ejecución o no se encontró CID. Continuamos con otras comprobaciones."
|
||||
fi
|
||||
|
||||
echo "== archivo local ./config.local.yml (si existe) =="
|
||||
if [ -f ./config.local.yml ]; then
|
||||
sed -n '1,200p' ./config.local.yml || true
|
||||
else
|
||||
echo "no existe ./config.local.yml"
|
||||
fi
|
||||
|
||||
echo "== inspeccionar volumen libretime_config =="
|
||||
docker run --rm -v libretime_config:/tmp alpine sh -c 'ls -la /tmp || true; sed -n "1,200p" /tmp/config.yml || true' || true
|
||||
|
||||
echo "== recrear config-generator (no-deps) para intentar generar config.yml =="
|
||||
docker compose -f docker-compose.easypanel.yml up -d --no-deps --force-recreate config-generator || true
|
||||
sleep 2
|
||||
docker compose -f docker-compose.easypanel.yml logs --tail 200 config-generator || true
|
||||
|
||||
echo "== volver a inspeccionar volumen libretime_config =="
|
||||
docker run --rm -v libretime_config:/tmp alpine sh -c 'ls -la /tmp || true; sed -n "1,200p" /tmp/config.yml || true' || true
|
||||
|
||||
if [ "${1:-}" = "--force-delete" ]; then
|
||||
echo "\n== OPCIÓN: eliminar y regenerar el volumen libretime_config (se pide confirmación) =="
|
||||
read -p "¿Deseas detener el stack y borrar el volumen libretime_config? (y/N): " confirm
|
||||
if [ "$confirm" = "y" ]; then
|
||||
echo "Deteniendo stack..."
|
||||
docker compose -f docker-compose.easypanel.yml down || true
|
||||
|
||||
mkdir -p backups
|
||||
echo "Haciendo backup (si existe) del config del volumen a ./backups/config.yml.bak"
|
||||
docker run --rm -v libretime_config:/tmp -v "$(pwd)/backups":/backup alpine sh -c 'cp -a /tmp/config.yml /backup/config.yml.bak || true; ls -la /tmp || true' || true
|
||||
|
||||
echo "Eliminando volumen libretime_config..."
|
||||
docker volume rm libretime_config || true
|
||||
|
||||
echo "Regenerando config-generator para crear un nuevo config.yml en el volumen limpio..."
|
||||
docker compose -f docker-compose.easypanel.yml up -d --no-deps --force-recreate config-generator || true
|
||||
sleep 2
|
||||
docker compose -f docker-compose.easypanel.yml logs --tail 200 config-generator || true
|
||||
echo "Contenido del nuevo volumen:"
|
||||
docker run --rm -v libretime_config:/tmp alpine sh -c 'ls -la /tmp; sed -n "1,200p" /tmp/config.yml || true' || true
|
||||
|
||||
echo "Listo. Reinicia la API con: docker compose -f docker-compose.easypanel.yml up -d --no-deps --force-recreate api"
|
||||
else
|
||||
echo "Abandonado: no se eliminará el volumen."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "== fin del script =="
|
||||
@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# Genera config.local.yml para LibreTime desde variables de entorno.
|
||||
# No guarda secretos en el repositorio.
|
||||
|
||||
CFG_PATH=${1:-./config.local.yml}
|
||||
|
||||
# Generar claves si no están provistas
|
||||
API_KEY=${LIBRETIME_API_KEY:-}
|
||||
SECRET_KEY=${LIBRETIME_SECRET_KEY:-}
|
||||
if [ -z "$API_KEY" ]; then
|
||||
API_KEY=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | cut -c1-40)
|
||||
fi
|
||||
if [ -z "$SECRET_KEY" ]; then
|
||||
SECRET_KEY=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | cut -c1-40)
|
||||
fi
|
||||
|
||||
cat > "$CFG_PATH" <<EOF
|
||||
general:
|
||||
public_url: "${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}"
|
||||
api_key: "${API_KEY}"
|
||||
secret_key: "${SECRET_KEY}"
|
||||
|
||||
database:
|
||||
name: "${POSTGRES_DB:-libretime}"
|
||||
user: "${POSTGRES_USER:-libretime}"
|
||||
password: "${POSTGRES_PASSWORD:-libretime}"
|
||||
host: "${POSTGRES_HOST:-postgres}"
|
||||
port: ${POSTGRES_PORT:-5432}
|
||||
|
||||
rabbitmq:
|
||||
host: "${RABBITMQ_HOST:-rabbitmq}"
|
||||
user: "${RABBITMQ_DEFAULT_USER:-libretime}"
|
||||
password: "${RABBITMQ_DEFAULT_PASS:-libretime}"
|
||||
vhost: "${RABBITMQ_DEFAULT_VHOST:-/libretime}"
|
||||
|
||||
icecast:
|
||||
source_password: "${ICECAST_SOURCE_PASSWORD:-changeme}"
|
||||
admin_password: "${ICECAST_ADMIN_PASSWORD:-changeme}"
|
||||
relay_password: "${ICECAST_RELAY_PASSWORD:-changeme}"
|
||||
admin_user: "${ICECAST_ADMIN_USER:-admin}"
|
||||
hostname: "${ICECAST_HOSTNAME:-localhost}"
|
||||
EOF
|
||||
|
||||
echo "Wrote $CFG_PATH"
|
||||
@ -1,60 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const templateDirDefault = path.resolve(__dirname, '..', 'easypanel-template-libretime');
|
||||
const templateDirSecure = path.resolve(__dirname, '..', 'easypanel-template-libretime-secure');
|
||||
const outFile = path.resolve(__dirname, '..', 'easypanel-template.json');
|
||||
const outFileSecure = path.resolve(__dirname, '..', 'easypanel-template-secure.json');
|
||||
|
||||
function isBinary(buf) {
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
if (buf[i] === 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function walk(dir, base) {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
const files = [];
|
||||
for (const e of entries) {
|
||||
const full = path.join(dir, e.name);
|
||||
const rel = path.join(base, e.name);
|
||||
if (e.isDirectory()) {
|
||||
files.push(...walk(full, rel));
|
||||
} else {
|
||||
const buf = fs.readFileSync(full);
|
||||
if (isBinary(buf)) {
|
||||
files.push({ path: rel.replace(/\\\\/g, '/'), encoding: 'base64', content: buf.toString('base64') });
|
||||
} else {
|
||||
files.push({ path: rel.replace(/\\\\/g, '/'), encoding: 'utf8', content: buf.toString('utf8') });
|
||||
}
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function generateFor(dir, outPath) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.error('Template dir not found:', dir);
|
||||
return;
|
||||
}
|
||||
|
||||
const metaPath = path.join(dir, 'meta.yaml');
|
||||
let metaYaml = '';
|
||||
if (fs.existsSync(metaPath)) {
|
||||
metaYaml = fs.readFileSync(metaPath, 'utf8');
|
||||
}
|
||||
|
||||
const files = walk(dir, '');
|
||||
|
||||
const out = {
|
||||
meta: metaYaml,
|
||||
files: files,
|
||||
};
|
||||
|
||||
fs.writeFileSync(outPath, JSON.stringify(out, null, 2), 'utf8');
|
||||
console.log('Wrote', outPath);
|
||||
}
|
||||
|
||||
generateFor(templateDirDefault, outFile);
|
||||
generateFor(templateDirSecure, outFileSecure);
|
||||
75
start.sh
75
start.sh
@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# start.sh - Flujo sencillo para usuarios finales:
|
||||
# 1) Cargar variables desde .env (si existe)
|
||||
# 2) Generar config.local.yml seguro desde variables de entorno
|
||||
# 3) Levantar postgres y rabbitmq
|
||||
# 4) Ejecutar migraciones
|
||||
# 5) Levantar el resto de servicios
|
||||
|
||||
set -e
|
||||
|
||||
if [ -f .env ]; then
|
||||
echo "Loading .env"
|
||||
# shellcheck disable=SC1091
|
||||
. .env
|
||||
fi
|
||||
|
||||
mkdir -p ./data
|
||||
echo "Generating config.local.yml"
|
||||
sh ./scripts/generate-config.sh ./config.local.yml
|
||||
|
||||
# Si POSTGRES_PASSWORD está definido en .env, asegurarnos de que config.local.yml use la misma contraseña
|
||||
if [ -n "${POSTGRES_PASSWORD:-}" ]; then
|
||||
echo "Asegurando que config.local.yml use la contraseña de POSTGRES_PASSWORD desde .env"
|
||||
# Sustituir la línea password: "..." en la sección database por el valor de POSTGRES_PASSWORD
|
||||
# Usamos awk para editar de forma segura el fichero yaml simple
|
||||
# Hacer copia de seguridad si existe
|
||||
if [ -f ./config.local.yml ]; then
|
||||
ts=$(date +%Y%m%d%H%M%S)
|
||||
cp ./config.local.yml ./config.local.yml.bak.$ts
|
||||
echo "Backup creado: ./config.local.yml.bak.$ts"
|
||||
fi
|
||||
|
||||
awk -v pw="$POSTGRES_PASSWORD" '
|
||||
BEGIN {in_db=0}
|
||||
/^database:/ {in_db=1; print; next}
|
||||
in_db && /^[[:space:]]*password:/ {print " password: \"" pw "\""; in_db=0; next}
|
||||
{print}
|
||||
' ./config.local.yml > ./config.local.yml.tmp && mv ./config.local.yml.tmp ./config.local.yml
|
||||
echo "config.local.yml actualizada con la contraseña de .env"
|
||||
fi
|
||||
|
||||
echo "Starting postgres and rabbitmq..."
|
||||
docker compose -f docker-compose.simple.yml up -d postgres rabbitmq
|
||||
|
||||
echo "Waiting for postgres container id..."
|
||||
POSTGRES_CID=""
|
||||
for i in $(seq 1 30); do
|
||||
POSTGRES_CID=$(docker compose -f docker-compose.simple.yml ps -q postgres || true)
|
||||
if [ -n "$POSTGRES_CID" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
if [ -z "$POSTGRES_CID" ]; then
|
||||
echo "Error: no pude encontrar el contenedor postgres" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Esperando a que Postgres acepte conexiones (pg_isready dentro del contenedor)..."
|
||||
for i in $(seq 1 120); do
|
||||
if docker exec "$POSTGRES_CID" pg_isready -U ${POSTGRES_USER:-libretime} >/dev/null 2>&1; then
|
||||
echo "Postgres listo"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
|
||||
echo "Applying migrations"
|
||||
docker compose -f docker-compose.simple.yml run --rm migrate
|
||||
|
||||
echo "Starting remaining services"
|
||||
docker compose -f docker-compose.simple.yml up -d --remove-orphans nginx api legacy playout liquidsoap worker analyzer icecast
|
||||
|
||||
echo "Done. Browse to ${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}"
|
||||
@ -1,25 +0,0 @@
|
||||
general:
|
||||
public_url: "https://libretime.example.com"
|
||||
api_key: ""
|
||||
secret_key: ""
|
||||
|
||||
database:
|
||||
host: postgres
|
||||
port: 5432
|
||||
name: libretime
|
||||
user: libretime
|
||||
password: "secretpass"
|
||||
|
||||
rabbitmq:
|
||||
host: rabbitmq
|
||||
port: 5672
|
||||
vhost: /libretime
|
||||
user: libretime
|
||||
password: "rabbitpass"
|
||||
|
||||
icecast:
|
||||
source_password: "srcpass"
|
||||
admin_password: "adminpass"
|
||||
relay_password: "changeme"
|
||||
admin_user: "admin"
|
||||
hostname: "localhost"
|
||||
@ -1,39 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Script de prueba: genera config en ./tmp_config/config.yml para validar el contenido
|
||||
|
||||
set -eu
|
||||
|
||||
CONFIG_DIR=./tmp_config
|
||||
CONFIG_PATH="$CONFIG_DIR/config.yml"
|
||||
|
||||
mkdir -p "$CONFIG_DIR"
|
||||
|
||||
cat > "$CONFIG_PATH" <<EOF
|
||||
general:
|
||||
public_url: "${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}"
|
||||
api_key: "${LIBRETIME_API_KEY:-}"
|
||||
secret_key: "${LIBRETIME_SECRET_KEY:-}"
|
||||
|
||||
database:
|
||||
host: ${POSTGRES_HOST:-postgres}
|
||||
port: ${POSTGRES_PORT:-5432}
|
||||
name: ${POSTGRES_DB:-libretime}
|
||||
user: ${POSTGRES_USER:-libretime}
|
||||
password: "${POSTGRES_PASSWORD:-}"
|
||||
|
||||
rabbitmq:
|
||||
host: ${RABBITMQ_HOST:-rabbitmq}
|
||||
port: ${RABBITMQ_PORT:-5672}
|
||||
vhost: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
user: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
password: "${RABBITMQ_DEFAULT_PASS:-}"
|
||||
|
||||
icecast:
|
||||
source_password: "${ICECAST_SOURCE_PASSWORD:-changeme}"
|
||||
admin_password: "${ICECAST_ADMIN_PASSWORD:-changeme}"
|
||||
relay_password: "${ICECAST_RELAY_PASSWORD:-changeme}"
|
||||
admin_user: "${ICECAST_ADMIN_USER:-admin}"
|
||||
hostname: "${ICECAST_HOSTNAME:-localhost}"
|
||||
EOF
|
||||
|
||||
echo "wrote $CONFIG_PATH"
|
||||
@ -1,52 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Generador de config para EasyPanel
|
||||
# Lee variables de entorno y escribe /config/config.yml de forma atómica
|
||||
|
||||
set -eu
|
||||
|
||||
CONFIG_PATH=/config/config.yml
|
||||
TMP_PATH=/config/config.yml.tmp
|
||||
|
||||
# Generar usando heredoc sin comillas para permitir expansión de variables
|
||||
cat > "$TMP_PATH" <<EOF
|
||||
general:
|
||||
public_url: "${LIBRETIME_GENERAL_PUBLIC_URL:-http://localhost:8080}"
|
||||
api_key: "${LIBRETIME_API_KEY:-}"
|
||||
secret_key: "${LIBRETIME_SECRET_KEY:-}"
|
||||
|
||||
database:
|
||||
host: "${POSTGRES_HOST:-postgres}"
|
||||
port: ${POSTGRES_PORT:-5432}
|
||||
name: "${POSTGRES_DB:-libretime}"
|
||||
user: "${POSTGRES_USER:-libretime}"
|
||||
password: "${POSTGRES_PASSWORD:-}"
|
||||
|
||||
rabbitmq:
|
||||
host: "${RABBITMQ_HOST:-rabbitmq}"
|
||||
port: ${RABBITMQ_PORT:-5672}
|
||||
vhost: "${RABBITMQ_DEFAULT_VHOST:-/libretime}"
|
||||
user: "${RABBITMQ_DEFAULT_USER:-libretime}"
|
||||
password: "${RABBITMQ_DEFAULT_PASS:-}"
|
||||
|
||||
icecast:
|
||||
source_password: "${ICECAST_SOURCE_PASSWORD:-changeme}"
|
||||
admin_password: "${ICECAST_ADMIN_PASSWORD:-changeme}"
|
||||
relay_password: "${ICECAST_RELAY_PASSWORD:-changeme}"
|
||||
admin_user: "${ICECAST_ADMIN_USER:-admin}"
|
||||
hostname: "${ICECAST_HOSTNAME:-localhost}"
|
||||
EOF
|
||||
|
||||
# Validación mínima: debe contener la clave 'general:'
|
||||
if ! grep -q '^general:' "$TMP_PATH"; then
|
||||
echo "ERROR: config generation failed (missing 'general:')"
|
||||
rm -f "$TMP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mover de forma atómica
|
||||
mv "$TMP_PATH" "$CONFIG_PATH"
|
||||
|
||||
echo "wrote $CONFIG_PATH"
|
||||
|
||||
# Mantener el contenedor en ejecución para que dependientes puedan verificar salud
|
||||
tail -f /dev/null
|
||||
@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# restore-config.sh
|
||||
# Lista y restaura backups creados por start.sh (config.local.yml.bak.YYYYMMDDHHMMSS)
|
||||
|
||||
set -e
|
||||
|
||||
WORKDIR="$(pwd)"
|
||||
BACKUP_GLOB="$WORKDIR/config.local.yml.bak.*"
|
||||
DEST="$WORKDIR/config.local.yml"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [--list] [--latest] [--restore <file>] [--yes]
|
||||
|
||||
Options:
|
||||
--list List available backup files
|
||||
--latest Show the latest backup file (implies --list)
|
||||
--restore FILE Restore the specified backup into config.local.yml
|
||||
--yes Skip confirmation when restoring
|
||||
|
||||
Examples:
|
||||
$0 --list
|
||||
$0 --latest
|
||||
$0 --restore ./config.local.yml.bak.20251001102251
|
||||
$0 --restore latest --yes
|
||||
EOF
|
||||
}
|
||||
|
||||
list_backups() {
|
||||
ls -1 $BACKUP_GLOB 2>/dev/null | sort || true
|
||||
}
|
||||
|
||||
latest_backup() {
|
||||
# sort by name (timestamp suffix) and pick last
|
||||
ls -1 $BACKUP_GLOB 2>/dev/null | sort | tail -n 1 || true
|
||||
}
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FORCE=0
|
||||
MODE=""
|
||||
TARGET=""
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--list)
|
||||
MODE=list; shift ;;
|
||||
--latest)
|
||||
MODE=latest; shift ;;
|
||||
--restore)
|
||||
MODE=restore; TARGET="$2"; shift 2 ;;
|
||||
--yes)
|
||||
FORCE=1; shift ;;
|
||||
-h|--help)
|
||||
usage; exit 0 ;;
|
||||
*)
|
||||
echo "Unknown arg: $1" >&2; usage; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$MODE" in
|
||||
list)
|
||||
echo "Backups:";
|
||||
list_backups;
|
||||
exit 0
|
||||
;;
|
||||
latest)
|
||||
echo "Latest backup:";
|
||||
latest_backup;
|
||||
exit 0
|
||||
;;
|
||||
restore)
|
||||
if [ "$TARGET" = "latest" ] || [ -z "$TARGET" ]; then
|
||||
TARGET=$(latest_backup)
|
||||
fi
|
||||
if [ -z "$TARGET" ] || [ ! -f "$TARGET" ]; then
|
||||
echo "No backup found to restore: $TARGET" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "About to restore backup: $TARGET -> $DEST"
|
||||
if [ "$FORCE" -ne 1 ]; then
|
||||
printf "Continue and overwrite %s? [y/N]: " "$DEST"
|
||||
read ans || true
|
||||
case "$ans" in
|
||||
y|Y|yes|YES)
|
||||
;;
|
||||
*)
|
||||
echo "Aborted."; exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
cp "$TARGET" "$DEST"
|
||||
echo "Restored $TARGET -> $DEST"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
usage; exit 1 ;;
|
||||
esac
|
||||
Loading…
x
Reference in New Issue
Block a user