diff --git a/Dockerfile b/Dockerfile index 346d011..2f3450f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,5 +14,5 @@ COPY . . # Puerto de la aplicación EXPOSE 3000 -# Comando de inicio -CMD ["npm", "start"] +# Ejecutar migraciones, seed y luego iniciar la app +CMD ["sh", "-c", "npm run migrate:up && npm run seed && npm start"] diff --git a/db/migrations/20260204180834_seed_tenant_piaf.sql b/db/migrations/20260204180834_seed_tenant_piaf.sql new file mode 100644 index 0000000..9fad1f6 --- /dev/null +++ b/db/migrations/20260204180834_seed_tenant_piaf.sql @@ -0,0 +1,13 @@ +-- migrate:up +-- Crear tenant Piaf (sin credenciales sensibles - esas van por variable de entorno) + +INSERT INTO tenants (id, key, name) +VALUES ( + 'eb71b9a7-9ccf-430e-9b25-951a0c589c0f'::uuid, + 'piaf', + 'Piaf' +) +ON CONFLICT (id) DO NOTHING; + +-- migrate:down +DELETE FROM tenants WHERE id = 'eb71b9a7-9ccf-430e-9b25-951a0c589c0f'::uuid; diff --git a/docker-compose.yaml b/docker-compose.yaml index 952799e..e2cc9d8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,6 +9,11 @@ services: - PORT=3000 - DATABASE_URL=postgres://${POSTGRES_USER:-botino}:${POSTGRES_PASSWORD:-botino}@db:5432/${POSTGRES_DB:-botino} - REDIS_URL=redis://redis:6379 + # Variables para seed (configurar en Coolify) + - APP_ENCRYPTION_KEY=${APP_ENCRYPTION_KEY:-} + - WOO_CONSUMER_KEY=${WOO_CONSUMER_KEY:-} + - WOO_CONSUMER_SECRET=${WOO_CONSUMER_SECRET:-} + - WOO_BASE_URL=${WOO_BASE_URL:-} depends_on: db: condition: service_healthy diff --git a/env.example b/env.example index 935aad4..970376b 100644 --- a/env.example +++ b/env.example @@ -26,7 +26,9 @@ OPENAI_MODEL=gpt-4o-mini TURN_ENGINE=v1 # =================== -# WooCommerce (fallback si falta config por tenant) +# WooCommerce (usado por seed y fallback) +# Estas variables son leídas por scripts/seed-tenant.mjs para crear +# la configuración inicial del tenant en la base de datos. # =================== WOO_BASE_URL=https://tu-tienda.com WOO_CONSUMER_KEY=ck_xxx diff --git a/package.json b/package.json index 7fd888e..14c85c5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "migrate:up": "dbmate up", "migrate:down": "dbmate down", "migrate:redo": "dbmate rollback && dbmate up", - "migrate:status": "dbmate status" + "migrate:status": "dbmate status", + "seed": "node scripts/seed-tenant.mjs" }, "keywords": [], "author": "Lucas Tettamanti", @@ -22,6 +23,7 @@ "ajv": "^8.17.1", "cors": "^2.8.5", "csv-parse": "^6.1.0", + "dbmate": "^2.0.0", "dotenv": "^17.2.3", "express": "^4.19.2", "mysql2": "^3.16.2", @@ -32,7 +34,6 @@ }, "devDependencies": { "@vitest/coverage-v8": "^4.0.18", - "dbmate": "^2.0.0", "nodemon": "^3.0.3", "vitest": "^4.0.18" } diff --git a/scripts/seed-tenant.mjs b/scripts/seed-tenant.mjs new file mode 100644 index 0000000..798ec62 --- /dev/null +++ b/scripts/seed-tenant.mjs @@ -0,0 +1,93 @@ +#!/usr/bin/env node +/** + * Seed script para configurar tenant con credenciales de WooCommerce. + * Lee las credenciales de variables de entorno (no hardcodeadas). + * + * Variables requeridas: + * - DATABASE_URL: conexión a PostgreSQL + * - APP_ENCRYPTION_KEY: clave para encriptar credenciales + * - WOO_CONSUMER_KEY: consumer key de WooCommerce + * - WOO_CONSUMER_SECRET: consumer secret de WooCommerce + * - WOO_BASE_URL: URL base de WooCommerce (opcional, default: https://piaf.floda.dev/wp-json/wc/v3) + */ + +import pg from "pg"; + +const TENANT_ID = "eb71b9a7-9ccf-430e-9b25-951a0c589c0f"; + +async function seed() { + const { + DATABASE_URL, + APP_ENCRYPTION_KEY, + WOO_CONSUMER_KEY, + WOO_CONSUMER_SECRET, + WOO_BASE_URL = "https://piaf.floda.dev/wp-json/wc/v3", + } = process.env; + + // Validar variables requeridas + if (!DATABASE_URL) { + console.log("[seed] DATABASE_URL no configurada, saltando seed"); + return; + } + + if (!APP_ENCRYPTION_KEY || !WOO_CONSUMER_KEY || !WOO_CONSUMER_SECRET) { + console.log("[seed] Variables de WooCommerce no configuradas, saltando seed de ecommerce config"); + console.log("[seed] Para configurar, definir: APP_ENCRYPTION_KEY, WOO_CONSUMER_KEY, WOO_CONSUMER_SECRET"); + return; + } + + const pool = new pg.Pool({ connectionString: DATABASE_URL }); + + try { + // Verificar si ya existe la config + const check = await pool.query( + "SELECT 1 FROM tenant_ecommerce_config WHERE tenant_id = $1", + [TENANT_ID] + ); + + if (check.rows.length > 0) { + console.log("[seed] tenant_ecommerce_config ya existe, saltando"); + return; + } + + // Configurar encryption key para la sesión + await pool.query("SELECT set_config('app.encryption_key', $1, false)", [ + APP_ENCRYPTION_KEY, + ]); + + // Insertar config de WooCommerce + await pool.query( + `INSERT INTO tenant_ecommerce_config ( + tenant_id, + provider, + base_url, + credential_ref, + api_version, + timeout_ms, + enabled, + enc_consumer_key, + enc_consumer_secret + ) VALUES ( + $1::uuid, + 'woo', + $2, + 'secret://woo/piaf', + 'wc/v3', + 8000, + true, + pgp_sym_encrypt($3, current_setting('app.encryption_key')), + pgp_sym_encrypt($4, current_setting('app.encryption_key')) + )`, + [TENANT_ID, WOO_BASE_URL, WOO_CONSUMER_KEY, WOO_CONSUMER_SECRET] + ); + + console.log("[seed] tenant_ecommerce_config creada exitosamente"); + } catch (err) { + console.error("[seed] Error:", err.message); + // No fallar el startup si el seed falla (puede ser que las tablas no existan aún) + } finally { + await pool.end(); + } +} + +seed();