Despliega Ticketory
Pon en línea el sitio web, el panel y la API: Docker Compose en un servidor, una aplicación por host desde su Dockerfile, o Node.js sin más.
Para el paquete Full Stack
Tres formas de desplegar
| Forma | Ideal para | Qué necesitas |
|---|---|---|
| Docker Compose en un servidor | Un VPS que controlas, las tres aplicaciones juntas | El paquete Full Stack, Docker en el servidor, un proxy inverso para HTTPS |
| Una aplicación por host, desde su Dockerfile | Railway, Render, Fly.io y otros hosts que construyen desde un Dockerfile | La carpeta de cada aplicación; un volumen o MySQL y un bucket para la API |
| Node.js sin más | Cualquier host con Node | Node.js 22 o más reciente y Yarn 4 en el host |
Elijas la forma que elijas, el sitio web y el panel compilan la dirección de la API en su JavaScript cuando se construyen, así que constrúyelos con las direcciones públicas, nunca con las locales.
Docker Compose en un servidor
Copia la carpeta del paquete al servidor y pon un .env junto a docker-compose.yml con las tres direcciones públicas. Los orígenes permitidos de la API, las direcciones a las que las páginas de pago devuelven a los compradores, los enlaces de descarga de las entradas, las direcciones de las imágenes y la dirección de la API de los frontends se construyen todos a partir de ellas. REVALIDATE_SECRET sustituye al valor local por defecto que comparten el sitio web y la API para actualizar las páginas de evento al instante.
SITE_URL=https://www.your-domain.com
ADMIN_URL=https://dashboard.your-domain.com
API_URL=https://api.your-domain.com
MEDIA_HOSTNAME=api.your-domain.com
REVALIDATE_SECRET=a-long-random-stringMEDIA_HOSTNAME es opcional, un host o varios separados por comas (añade el host de tu bucket cuando las subidas van a uno): las imágenes de esos hosts https las redimensiona Next.js, y cualquier otra imagen se carga tal cual. Luego construye e inicia en segundo plano:
docker compose up --build -d- Apunta cada dominio al servidor mediante un proxy inverso que sirva HTTPS (Caddy, Nginx o Traefik), reenviando a los puertos
3030,3031y8000. - Detrás de ese proxy, define
TRUST_PROXY=1enback-end/.env, para que los límites de inicio de sesión, formulario de contacto y escaneo de la puerta cuenten a cada visitante y no al proxy. El contenedor de la API leeback-end/.envcuando existe, que es también donde van las claves de pago, correo, IA y almacenamiento. - Sin un
JWT_SECRETpropio, la API genera uno en su primer inicio y lo guarda en el volumenticketory-data. La base de datos, las subidas y las exportaciones CSV también viven en ese volumen, así que haz una copia de seguridad. - El primer inicio en un volumen nuevo crea los organizadores de ejemplo salvo que
SEED_DEMO_DATA=falseesté en el.env. Para tus propios eventos, inicia sin ellos y con tu propio propietario, como en Puesta en marcha con tu propio espacio de trabajo, más abajo.
La API desde su Dockerfile
Apunta el host a la carpeta de la API (back-end/ en el Full Stack). La imagen escucha en PORT (8000 salvo que el host defina otro), responde GET /api/health en cuanto su base de datos responde, y guarda SQLite, las subidas y las exportaciones CSV en /data.
- Monta un volumen persistente en
/data. Sin uno, cada despliegue empieza con una base de datos vacía. - Define
CORS_ORIGINcon las direcciones del sitio web y del panel (separadas por comas),FRONTEND_URLcon la del panel,STOREFRONT_URLcon la del sitio web,PUBLIC_API_URLcon la dirección pública de la API yPUBLIC_MEDIA_URLcon esa dirección seguida de/media. - El valor por defecto de la imagen es
SEED_DEMO_DATA=false: un volumen nuevo recibe solo las tablas, y el primer inicio de la API crea entonces tu espacio de trabajo y su propietario a partir deOWNER_EMAILyOWNER_PASSWORD(mira más abajo). DefineSEED_DEMO_DATA=trueen su lugar para tener los organizadores de ejemplo. Una base de datos existente nunca se vuelve a llenar con datos de ejemplo. - Define tu propio
JWT_SECRET, o deja que el contenedor genere uno y lo guarde en/data/.jwt-secret. - Define
TRUST_PROXY=1: una plataforma de alojamiento pone un proxy delante del contenedor. - En MySQL (
DB_TYPE=mysqly las cinco variablesDB_*), el contenedor no crea las tablas. Ejecuta una veznode dist/database/seeder.js(datos de ejemplo) onode dist/database/sync-schema.js(tablas vacías) con el mismo entorno. - Las subidas en el volumen sobreviven a un nuevo despliegue; para mantenerlas fuera del servidor, define las cinco variables
R2_*para un bucket. - Define
REVALIDATE_SECRETcon el mismo valor largo y aleatorio en la API y en el sitio web, ySTOREFRONT_INTERNAL_URLcuando la API llegue al sitio web en una dirección distinta deSTOREFRONT_URL.
El sitio web y el panel desde sus Dockerfiles
Apunta el host a la carpeta de la aplicación y dale los valores NEXT_PUBLIC_* como argumentos de construcción, no como variables de ejecución: se compilan dentro. Cada imagen sirve el servidor independiente de Next.js en PORT (3030 para el sitio web, 3031 para el panel, salvo que el host defina otro).
| Argumento de construcción | Aplicación | Valor |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL | Ambos | La dirección pública de la API seguida de /api |
NEXT_PUBLIC_SITE_URL | Ambos | La dirección pública del sitio web: las URL canónicas y el mapa del sitio en el sitio web, el enlace "Ver sitio web" y las vistas previas de eventos en el panel |
NEXT_PUBLIC_DASHBOARD_URL | Ambos | La dirección pública del panel: adonde el "Sell tickets" del sitio web envía a los organizadores, y la imagen para compartir del propio panel |
NEXT_PUBLIC_MEDIA_HOSTNAME | Ambos | Opcional. Los hosts https que sirven las imágenes (el dominio de la API, el de tu bucket, o ambos), separados por comas |
NEXT_PUBLIC_WEBSOCKET_BASE_URL | Panel | La dirección pública de la API sin /api, para las notificaciones en directo |
El servidor del sitio web también consulta la API mientras renderiza las páginas de evento, recinto y organizador y el mapa del sitio. Con una dirección pública de la API que también funcione desde el servidor, no hace falta nada más; API_INTERNAL_URL en tiempo de ejecución la anula cuando el servidor debe usar otra dirección. REVALIDATE_SECRET también es una variable de ejecución del sitio web, nunca un argumento de construcción.
Node.js sin más
Construye cada aplicación una vez en el host y luego inicia. Define el .env de los frontends con las direcciones públicas antes de yarn build.
| Aplicación | Construcción | Inicio |
|---|---|---|
| API | yarn build, y luego una vez yarn seed:prod (datos de ejemplo) o yarn db:sync:prod (tablas vacías) | yarn start:prod |
| Sitio web público | yarn build | yarn start (puerto 3030) |
| Panel del organizador | yarn build | yarn start (puerto 3031) |
La API se enlaza a 0.0.0.0 y lee PORT. Mantén el archivo SQLite y MEDIA_UPLOAD_DIR en un almacenamiento que sobreviva a un despliegue, o usa MySQL y un bucket.
Puesta en marcha con tu propio espacio de trabajo
Los organizadores de ejemplo y sus inicios de sesión sirven para probar Ticketory. Para tus propios eventos, empieza con una base de datos nueva sin ellos: en una base de datos sin cuenta de personal ni espacio de trabajo, el primer inicio de la API crea los datos base (los 29 permisos, los cuatro roles, los ajustes del sitio y las tres categorías de eventos), un espacio de trabajo vacío y su propietario.
Define las variables del propietario
En
back-end/.env, o en la configuración de tu host para la API. Con Docker Compose, ponlas en el.envjunto adocker-compose.ymljunto conSEED_DEMO_DATA=false: Compose lee ese archivo primero y luegoback-end/.env, cuyo valor gana donde ambos definen uno.back-end/.envOWNER_EMAIL=you@your-domain.com OWNER_PASSWORD=a-long-password-of-your-own OWNER_NAME=Your Name WORKSPACE_NAME=Your organisationResultado esperado:
OWNER_NAMEyWORKSPACE_NAMEson opcionales (OwneryTicketorysi no se definen), y ambos se cambian después, en tu perfil y en Settings, General. La contraseña necesita al menos 10 caracteres, y conNODE_ENV=productionse rechazan el valor del ejemplo de.env.exampley la contraseña de las cuentas de ejemplo.Empieza sin los datos de ejemplo, en una base de datos nueva
Sin Docker, en la carpeta de la API:
yarn build, luegoyarn db:sync:prod(noyarn seed), luegoyarn start:prod. Con Docker Compose, manténSEED_DEMO_DATA=falseen el.envjunto adocker-compose.ymly ejecutadocker compose up --build -dsobre un volumen nuevo (docker compose down -vborra uno existente, con todos sus datos). Un contenedor de la API solo arranca sin-e SEED_DEMO_DATA=true, con las variables pasadas con--env-file .envo-e.Resultado esperado: El registro de la API dice
¿Tienes problemas?Clean start: created the workspace "Your organisation" and its Owner, con el correo.Inicia sesión y configura el espacio de trabajo
Inicia sesión en el panel con
OWNER_EMAILyOWNER_PASSWORD. El espacio de trabajo está vacío: añade un recinto y luego tu primer evento. Su moneda es USD hasta que la cambies en Settings, General, lo que puedes hacer hasta que se pague la primera entrada. Después invita a tu equipo desde la página Team, a cada uno con el rol que necesita.
- Ocurre una sola vez. En cuanto existe cualquier cuenta de personal, las variables del propietario se ignoran, así que un reinicio o una segunda copia de la API no crea nada más, y después puedes quitar
OWNER_PASSWORDde la configuración. La contraseña nunca se imprime. yarn seedy una base de datos llena con los datos de ejemplo nunca las leen: esa base de datos ya tiene personal.- El panel no tiene página de registro. Cada otra cuenta de personal se añade con Invite member en la página Team, con un rol y una contraseña que compartes tú mismo (no se envía nada por correo).
Empezar en cambio desde los datos de ejemplo
Una base de datos llena con los datos de ejemplo conserva sus cuatro organizadores y sus cuentas. Si empiezas desde ella, hazla tuya antes de que sea accesible desde internet:
- Haz tuyo al propietario de ejemplo: cambia su nombre y correo en Settings, Profile, y su contraseña en Settings, Security. Es la única cuenta que pertenece a todos los espacios de trabajo de ejemplo, y ninguna pantalla añade un miembro existente a otro espacio de trabajo, así que consérvala en lugar de sustituirla por una cuenta nueva.
- Quita de la página Team de cada espacio de trabajo a los miembros de ejemplo que no necesites, o cambia sus contraseñas. Remove saca a un miembro del espacio de trabajo en el que estás; la cuenta se borra con su último espacio de trabajo. No puedes eliminarte a ti mismo, y cada espacio de trabajo conserva un propietario.
- Define el nombre del espacio de trabajo, el correo de soporte, la moneda, la comisión de reserva, la zona horaria y las notificaciones del equipo en Settings, General y Notifications. La moneda se bloquea cuando se cobra el primer pedido pagado.
- Archiva o cancela los eventos de ejemplo y sustituye los recintos de ejemplo por los tuyos, con sus planos de asientos. No hay pantalla ni ruta para crear otro espacio de trabajo, así que renombra uno en Settings, General para tu propio negocio.
- Los asistentes crean sus propias cuentas en el sitio web, o compran como invitados. El asistente de ejemplo son datos de ejemplo: bórralo con el resto.
Actualizar una instalación que ya usas
Con NODE_ENV=production, la API nunca cambia las tablas por sí misma. Tras una actualización que añade una columna o una tabla, sincroniza el esquema una vez en la base de datos existente, antes de iniciar la nueva versión. Esta añade la columna locale en orders (el idioma de los correos de cada pedido) y la tabla event_reminders.
Sin Docker, en la carpeta de la API:
yarn build
yarn db:sync:prodNo escribe filas y conserva tus datos. Con Docker, el contenedor deja en paz una base de datos existente, así que tras docker compose up --build -d ejecuta el mismo script una vez en el contenedor de la API en marcha, y luego reinícialo (para un contenedor de la API solo, docker exec -u node <container> node dist/database/sync-schema.js, y luego docker restart <container>):
docker compose exec -u node api node dist/database/sync-schema.js
docker compose restart apiCon NODE_ENV=development, como con yarn dev, la API actualiza las tablas por sí sola al iniciar.
Pasar a producción
Antes de que Ticketory sea accesible desde internet:
- En el entorno de la API,
NODE_ENV=productiony unJWT_SECRETlargo y aleatorio propio (Docker genera uno cuando no hay ninguno). En producción la API se niega a iniciar sinCORS_ORIGINy dice qué valor falta. CORS_ORIGINcon el sitio web y el panel,FRONTEND_URLcon el panel,STOREFRONT_URLcon el sitio web,PUBLIC_API_URLcon la API yPUBLIC_MEDIA_URLcon la API seguida de/media.TICKET_SECRETdefinido una vez, antes de la primera venta: firma los códigos QR, y cambiarlo después anula todas las entradas ya vendidas.- Tus claves reales de Stripe o PayPal, y los webhooks registrados en cada proveedor en
/api/webhooks/stripey/api/webhooks/paypalen la dirección de la API. Con una clave real definida, Test payment desaparece del pago. - Impuestos configurados para tu país: Ticketory no cobra ninguno por sí mismo.
RESEND_API_KEYyMAIL_FROM, o los asistentes no reciben correos de entradas, códigos de registro ni restablecimientos de contraseña.- La base de datos, las subidas y las exportaciones CSV en almacenamiento persistente, con copia de seguridad.
- La comprobación de estado del host en
/api/health, yTRUST_PROXY=1detrás de un proxy. - Ambos frontends construidos con los valores públicos
NEXT_PUBLIC_*, y el mismoREVALIDATE_SECRETen la API y en el sitio web. - En el sitio web, tu marca en
src/config/brand.config.ts, y los textos de privacidad, condiciones y cancelación enmessages/legalreescritos para tu negocio y tu país. - Tu propio espacio de trabajo desde un inicio limpio, o las contraseñas de ejemplo cambiadas y las cuentas de ejemplo que no necesitas eliminadas.
DEMO_MODE=falseen la API yNEXT_PUBLIC_DEMO_MODE=falseen ambos frontends, tal como se entrega.
No para datos de venta reales
yarn setup:fresh construye, elimina todas las tablas y carga los datos, cada vez que se ejecuta, y yarn db:reset elimina todas las tablas. Sirven para un entorno completamente nuevo, nunca como comando de inicio o de construcción.