Levantar el panel en local y entrar por primera vez
Al final de este tutorial vas a estar mirando la lista real de clientes de Gastro y Travel en tu navegador, con tu propio operador. Toma unos 10 minutos.
Se asume que tienes Docker corriendo y el monorepo clonado. No necesitas saber nada del panel.
1. Levanta la infraestructura compartida
El panel no tiene Postgres propio: usa el compartido del monorepo.
Si no ves timeliber-postgres, el resto no va a funcionar. Revisa el troubleshooting del
manual raíz.
2. Comprueba que existe operator_db
Postgres corre los scripts de infra/postgres/init/ solo la primera vez que se inicializa su
volumen. Si tu volumen es anterior a que existiera Operator, la base no está:
Si no aparece nada, créala corriendo el init a mano:
docker exec -i \
-e POSTGRES_USER=timeliber_admin -e POSTGRES_DB=postgres \
-e OPERATOR_DB_USER=operator_user \
-e OPERATOR_DB_PASSWORD="<el de infra/core/.env>" \
timeliber-postgres bash < infra/postgres/init/07_operator.sh
Ojo: no es idempotente. Si el rol ya existe, CREATE ROLE falla y el script aborta —
correcto, porque el caso "ya está creado" no debería sobreescribir nada en silencio.
Por qué un usuario aislado y no el admin maestro: si el BFF se compromete, el atacante llega hasta
operator_usersy no un centímetro más. Las bases de las verticales le quedan cerradas — ni siquiera tiene credenciales para ellas.
3. Configura el backend
Abre .env y llena tres cosas:
OPERATOR_JWT_SECRET— genera uno conopenssl rand -hex 32.OPERATOR_DB_PASSWORDy el password dentro deOPERATOR_DATABASE_URL— el mismo que está eninfra/core/.env.OPERATOR_GASTRO_INTERNAL_TOKENyOPERATOR_TRAVEL_INTERNAL_TOKEN— cópialos deGASTRO_INTERNAL_TOKEN(enapps/gastro/backend/.env) eINTERNAL_API_KEY(enapps/travel/backend/.env) respectivamente.
Si dejas un token vacío el panel arranca igual: esa vertical simplemente aparece como caída. Es degradación deliberada, no un error.
La referencia completa de variables está en 03_REFERENCE/environment.md.
4. Levanta el backend
cd ../../.. # raíz del monorepo
./manage.sh up operator_back
curl -s localhost:8012/api/v1/health/live
# {"status":"ok"}
5. Crea tu operador
No hay registro público: el panel administra clientes reales, así que los operadores se siembran a mano.
docker exec -it timeliber-operator-api python -m app.cli create-operator \
--email tu@correo.com --full-name "Tu Nombre"
Te va a pedir la contraseña por prompt (mínimo 12 caracteres). Se pide por prompt a propósito: pasarla por argumento la dejaría escrita en el historial de tu shell.
6. Abre el panel
Para desarrollo, corre el frontend con Vite apuntando a tu backend local:
cd apps/operator/frontend
npm install
echo 'VITE_API_URL=http://localhost:8012' > .env.local
npm run dev
Abre http://localhost:5173 y entra con el email y la contraseña del paso 5.
./manage.sh up operator_frontes otra cosa: construye la imagen nginx de producción, que no publica ningún puerto al host porque se sirve por Traefik enops.timeliber.com.co. Para mirar el panel en tu máquina, usanpm run dev.
Vas a ver el selector de verticales a la izquierda. Elige Gastro: aparecen los tenants reales con su plan, su estado y su fecha de vencimiento. Cambia a Travel y la misma tabla te muestra las properties — mismo idioma, aunque por debajo cada vertical hable distinto.
Qué acabas de montar
Tu navegador solo habló con el BFF. Fue el BFF quien, server-side, le preguntó a Gastro y a Travel por HTTP usando los tokens internos — que nunca salieron del servidor. Por eso el panel puede leer dos bases de datos a las que tu navegador no tiene, ni tendrá, acceso.
Siguiente paso
- Para cambiarle el plan a un cliente: Cambiarle el plan a un cliente.
- Para entender por qué el panel no toca las bases directamente: Por qué un BFF y no queries directas.