diff --git a/.env.development b/.env.development index 34b9183..ac7c399 100644 --- a/.env.development +++ b/.env.development @@ -4,21 +4,37 @@ COMPOSE_PROJECT_NAME=suitecoffee_dev # Entorno de desarrollo NODE_ENV=development -# app - app -APP_PORT=3030 -# auth - app -AUTH_PORT=4040 +MANSO_PORT=1010 # MVP Manso Microservicio -> services/manso/src/index.mjs +APP_PORT=3030 # Microservicio APP-> services/app/src/index.mjs +AUTH_PORT=4040 # Microservicio AUTH -> services/auth/src/index.mjs +PLUGINS_PORT=5050 # Microservicio PLUGINS-> services/plugins/src/index.mjs -# tenants - postgres -TENANTS_DB_NAME=dev-postgres -TENANTS_DB_USER=dev-user-postgres -TENANTS_DB_PASS=dev-pass-postgres - -# db primaria - postgres -DB_NAME=dev-suitecoffee +# ===== DB principal (metadatos de SuiteCoffee) ===== +# Usa el alias de red del servicio 'db' (compose: aliases [dev-db]) +DB_HOST=dev-db +DB_NAME=dev_suitecoffee_core +DB_PORT=5432 DB_USER=dev-user-suitecoffee DB_PASS=dev-pass-suitecoffee +CORE_DB_HOST=dev-db +CORE_DB_NAME=dev_suitecoffee_core +CORE_DB_PORT=5432 +CORE_DB_USER=dev-user-suitecoffee +CORE_DB_PASS=dev-pass-suitecoffee + +# ===== DB tenants (Tenants de SuiteCoffee) ===== +TENANTS_HOST=dev-tenants +TENANTS_DB=dev_suitecoffee_tenants +TENANTS_PORT=5432 +TENANTS_USER=suitecoffee +TENANTS_PASS=suitecoffee + +TENANTS_DB_HOST=dev-tenants +TENANTS_DB_NAME=dev_suitecoffee_tenants +TENANTS_DB_PORT=5432 +TENANTS_DB_USER=suitecoffee +TENANTS_DB_PASS=suitecoffee # Authentik PostgreSQL Setup AK_HOST_DB=ak-db AK_PG_DB=authentik @@ -26,12 +42,20 @@ AK_PG_USER=authentik AK_PG_PASS=gOWjL8V564vyh1aXUcqh4o/xo7eObraaCVZezPi3iw2LzPlU # Authentik Cookies -AUTHENTIK_COOKIE__DOMAIN=sso.suitecoffee.uy -AUTHENTIK_SECURITY__CSRF_TRUSTED_ORIGINS=https://sso.suitecoffee.uy,https://suitecoffee.uy +AUTHENTIK_COOKIE__DOMAIN=dev.sso.suitecoffee.uy +AUTHENTIK_SECURITY__CSRF_TRUSTED_ORIGINS=https://dev.sso.suitecoffee.uy,https://dev.suitecoffee.uy # Authentik Security AUTHENTIK_SECRET_KEY=11zMsUL57beO+okjeGh7OB3lQdGUWII+VaATHs/zsw1+6KMSTyGfAY0yHpq3C442+3CwrZ/KtjgHBfbv # Authentik Bootstrap AUTHENTIK_BOOTSTRAP_PASSWORD=info.suitecoffee@gmail.com -AUTHENTIK_BOOTSTRAP_EMAIL=info.suitecoffee@gmail.com \ No newline at end of file +AUTHENTIK_BOOTSTRAP_EMAIL=info.suitecoffee@gmail.com + +AUTHENTIK_EMAIL__HOST=smtp.gmail.com +AUTHENTIK_EMAIL__PORT=25 +AUTHENTIK_EMAIL__USERNAME=info.suitecoffee@gmail.com +AUTHENTIK_EMAIL__PASSWORD=Succulent-Sanded7 +AUTHENTIK_EMAIL__USE_TLS=true # Or false if not using TLS +AUTHENTIK_EMAIL__USE_SSL=false # Or true if using SSL directly +AUTHENTIK_EMAIL__FROM=info.suitecoffee@gmail.com \ No newline at end of file diff --git a/.env.production b/.env.production index 4834ca8..e9c13a3 100644 --- a/.env.production +++ b/.env.production @@ -1,26 +1,23 @@ # Archivo de variables de entorno para docker-compose.yml -COMPOSE_PROJECT_NAME=suitecoffee_prod +COMPOSE_PROJECT_NAME=suitecoffee_ # Entorno de desarrollo NODE_ENV=production -# app - app -APP_PORT=3000 - -# auth - app -AUTH_PORT=4000 +APP_PORT=3000 # Microservicio APP-> services/app/src/index.mjs +AUTH_PORT=4000 # Microservicio AUTH -> services/auth/src/index.mjs +PLUGIN_PORT=5000 # Microservicio PLUGINS-> services/plugins/src/index.mjs # tenants - postgres -TENANTS_DB_NAME=postgres -TENANTS_DB_USER=postgres -TENANTS_DB_PASS=postgres +TENANTS_DB_NAME=suitecoffee_tenants +TENANTS_DB_USER=suitecoffee +TENANTS_DB_PASS=suitecoffee # db primaria - postgres -DB_NAME=suitecoffee +DB_NAME=suitecoffee_core DB_USER=suitecoffee DB_PASS=suitecoffee - # Authentik PostgreSQL Setup AK_HOST_DB=ak-db AK_PG_DB=authentik diff --git a/compose.dev.yaml b/compose.dev.yaml index f19944b..322f6d8 100644 --- a/compose.dev.yaml +++ b/compose.dev.yaml @@ -4,13 +4,12 @@ services: app: - image: node:20-bookworm + image: node:20.19.5-bookworm working_dir: /app user: "${UID:-1000}:${GID:-1000}" volumes: - ./services/app:/app:rw - ./services/app/node_modules:/app/node_modules - # - ./services/shared:/app/shared env_file: - ./services/app/.env.development environment: @@ -22,26 +21,43 @@ services: aliases: [dev-app] command: npm run dev - # auth: - # image: node:20-bookworm - # working_dir: /app - # user: "${UID:-1000}:${GID:-1000}" - # volumes: - # - ./services/auth:/app:rw - # - ./services/auth/node_modules:/app/node_modules - # - ./services/shared:/app/shared - # env_file: - # - ./services/auth/.env.development - # environment: - # NODE_ENV: development # <- fuerza el entorno para que el loader tome .env.development - # expose: - # - ${AUTH_PORT} - # networks: - # net: - # aliases: [dev-auth] - # command: npm run dev + plugins: + image: node:20.19.5-bookworm + working_dir: /app + user: "${UID:-1000}:${GID:-1000}" + volumes: + - ./services/plugins:/app:rw + - ./services/plugins/node_modules:/app/node_modules + env_file: + - ./services/plugins/.env.development + environment: + NODE_ENV: development # <- fuerza el entorno para que el loader tome .env.development + expose: + - ${PLUGINS_PORT} + networks: + net: + aliases: [dev-plugins] + command: npm run dev - db: + auth: + image: node:20.19.5-bookworm + working_dir: /app + user: "${UID:-1000}:${GID:-1000}" + volumes: + - ./services/auth:/app:rw + - ./services/auth/node_modules:/app/node_modules + env_file: + - ./services/auth/.env.development + environment: + NODE_ENV: development # <- fuerza el entorno para que el loader tome .env.development + expose: + - ${AUTH_PORT} + networks: + net: + aliases: [dev-auth] + command: npm run dev + + dbCore: image: postgres:16 environment: POSTGRES_DB: ${DB_NAME} @@ -53,7 +69,7 @@ services: net: aliases: [dev-db] - tenants: + dbTenants: image: postgres:16 environment: POSTGRES_DB: ${TENANTS_DB_NAME} @@ -108,7 +124,6 @@ services: AUTHENTIK_BOOTSTRAP_PASSWORD: ${AUTHENTIK_BOOTSTRAP_PASSWORD} AUTHENTIK_BOOTSTRAP_EMAIL: ${AUTHENTIK_BOOTSTRAP_EMAIL} - AUTHENTIK_HTTP__TRUSTED_PROXY__CIDRS: "0.0.0.0/0,::/0" AUTHENTIK_SECURITY__CSRF_TRUSTED_ORIGINS: ${AUTHENTIK_SECURITY__CSRF_TRUSTED_ORIGINS} AUTHENTIK_COOKIE__DOMAIN: ${AUTHENTIK_COOKIE__DOMAIN} @@ -117,7 +132,7 @@ services: aliases: [dev-authentik] volumes: - ./authentik-media:/media - - ./authentik-custom-templates:/templates + - ./authentik-custom-templates:/templates ak-worker: image: ghcr.io/goauthentik/server:latest diff --git a/compose.manso.yaml b/compose.manso.yaml index 33f5a33..b05e098 100644 --- a/compose.manso.yaml +++ b/compose.manso.yaml @@ -1,4 +1,4 @@ -# docker-compose.overrride.yml +# compose.manso.yml # Docker Comose para entorno de desarrollo o development. @@ -11,8 +11,8 @@ services: # condition: service_healthy # tenants: # condition: service_healthy - # expose: - # - ${APP_LOCAL_PORT} + expose: + - ${MANSO_PORT} working_dir: /app user: "${UID:-1000}:${GID:-1000}" volumes: @@ -21,16 +21,16 @@ services: env_file: - ./services/manso/.env.development environment: - - NODE_ENV=${NODE_ENV} + NODE_ENV: development networks: net: aliases: [manso] - healthcheck: - test: ["CMD-SHELL", "curl -fsS http://localhost:${APP_PORT}/health || exit 1"] - interval: 10s - timeout: 3s - retries: 10 - start_period: 20s + #healthcheck: + # test: ["CMD-SHELL", "curl -fsS http://localhost:${MANSO_PORT}/health || exit 1"] + # interval: 10s + # timeout: 3s + # retries: 10 + # start_period: 20s command: npm run dev profiles: [manso] restart: unless-stopped diff --git a/compose.prod.yaml b/compose.prod.yaml index 4c990a2..dcae4e8 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -15,10 +15,27 @@ services: env_file: - ./services/app/.env.production environment: - - NODE_ENV=${NODE_ENV} + - NODE_ENV: production networks: net: - aliases: [prod-app] + aliases: [app] + command: npm run start + + plugins: + build: + context: ./services/plugins + dockerfile: Dockerfile.production + expose: + - ${PLUGIN_PORT} + volumes: + - ./services/plugins:/app + env_file: + - ./services/plugins/.env.production + environment: + - NODE_ENV: production + networks: + net: + aliases: [plugins] command: npm run start auth: @@ -32,40 +49,41 @@ services: env_file: - ./services/auth/.env.production environment: - - NODE_ENV=${NODE_ENV} - command: npm run start + - NODE_ENV: production networks: net: - aliases: [prod-auth] + aliases: [auth] + command: npm run start - db: + dbCore: image: postgres:16 environment: POSTGRES_DB: ${DB_NAME} POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASS} volumes: - - suitecoffee-db:/var/lib/postgresql/data + - dbCore_data:/var/lib/postgresql/data networks: net: - aliases: [prod-db] + aliases: [dbCore] - tenants: + dbTenants: image: postgres:16 environment: POSTGRES_DB: ${TENANTS_DB_NAME} POSTGRES_USER: ${TENANTS_DB_USER} POSTGRES_PASSWORD: ${TENANTS_DB_PASS} volumes: - - tenants-db:/var/lib/postgresql/data + - dbTenants_data:/var/lib/postgresql/data networks: net: - aliases: [prod-tenants] + aliases: [dbTenants] + +falta implementar authentik en compose.prod.yaml volumes: - tenants-db: - suitecoffee-db: - + dbCore_data: + dbTenants_data: networks: net: driver: bridge \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index 20429b8..c9063ab 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,33 +5,47 @@ name: ${COMPOSE_PROJECT_NAME:-suitecoffee} services: app: depends_on: - db: + dbCore: condition: service_healthy - tenants: + dbTenants: condition: service_healthy healthcheck: - test: ["CMD-SHELL", "curl -fsS http://localhost:${APP_PORT}/health || exit 1"] + test: ["CMD-SHELL", "curl -fsS http://localhost:${APP_PORT}/health || exit 1"] + interval: 10s + timeout: 3s + retries: 10 + start_period: 20s + restart: unless-stopped + + plugins: + depends_on: + app: + condition: service_healthy + auth: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:${PLUGINS_PORT}/health || exit 1"] interval: 10s timeout: 3s retries: 10 start_period: 20s restart: unless-stopped - # auth: - # depends_on: - # db: - # condition: service_healthy - # ak: - # condition: service_started - # healthcheck: - # test: ["CMD-SHELL", "curl -fsS http://localhost:${AUTH_PORT}/health || exit 1"] - # interval: 10s - # timeout: 3s - # retries: 10 - # start_period: 15s - # restart: unless-stopped + auth: + depends_on: + dbCore: + condition: service_healthy + ak: + condition: service_started + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:${AUTH_PORT}/health || exit 1"] + interval: 10s + timeout: 3s + retries: 10 + start_period: 20s + restart: unless-stopped - db: + dbCore: image: postgres:16 environment: TZ: America/Montevideo @@ -43,7 +57,7 @@ services: start_period: 10s restart: unless-stopped - tenants: + dbTenants: image: postgres:16 environment: TZ: America/Montevideo @@ -92,4 +106,4 @@ services: ak-redis: condition: service_healthy restart: unless-stopped - \ No newline at end of file + diff --git a/package.json b/package.json index 3094296..1979a65 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,8 @@ "name": "suitecoffee", "version": "1.0.0", "description": "Software para gestión de cafeterías", + "private": true, + "workspaces": [], "keywords": [ "coffee", "suite", diff --git a/packages/db/package.json b/packages/db/package.json new file mode 100644 index 0000000..31c995d --- /dev/null +++ b/packages/db/package.json @@ -0,0 +1,16 @@ +{ + "name": "@suitecoffee/db", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "Utilidades de acceso a Postgres para SuiteCoffee (pool por proceso + helpers multi-tenant).", + "exports": { + ".": "./src/index.mjs" + }, + "main": "./src/index.mjs", + "files": ["src"], + "sideEffects": false, + "peerDependencies": { + "pg": "^8.12.0" + } +} diff --git a/packages/db/src/index.mjs b/packages/db/src/index.mjs new file mode 100644 index 0000000..0364e56 --- /dev/null +++ b/packages/db/src/index.mjs @@ -0,0 +1,2 @@ +export * from './pool-registry.mjs'; +export * from './poolSingleton.mjs'; diff --git a/packages/db/src/pool-registry.mjs b/packages/db/src/pool-registry.mjs new file mode 100644 index 0000000..8251002 --- /dev/null +++ b/packages/db/src/pool-registry.mjs @@ -0,0 +1,54 @@ +import { Pool } from 'pg'; + +const REGISTRY = new Map(); + +export function getPool(name = 'core', cfg = {}) { + if (REGISTRY.has(name)) return REGISTRY.get(name); + + const pool = new Pool({ + connectionString: process.env.PG_URL, + max: Number(process.env.PG_POOL_MAX ?? 10), + idleTimeoutMillis: Number(process.env.PG_IDLE_MS ?? 30000), + connectionTimeoutMillis: Number(process.env.PG_CONN_MS ?? 5000), + statement_timeout: Number(process.env.PG_STMT_MS ?? 15000), + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + ...cfg + }); + + pool.on('error', (err) => { + // ideal: reemplazar con pino/sentry + console.error(`[pg:${name}] pool error`, err); + }); + + REGISTRY.set(name, pool); + return pool; +} + +function assertTenantSchema(schema) { + if (!/^tenant_[a-f0-9-]{16,36}$/i.test(schema)) { + throw new Error('Invalid tenant schema'); + } + return `"${schema.replace(/"/g, '""')}"`; +} + +export async function withTenant(poolName, tenantSchema, fn) { + const pool = getPool(poolName); + const client = await pool.connect(); + try { + await client.query('BEGIN'); + await client.query(`SET LOCAL search_path TO ${assertTenantSchema(tenantSchema)}`); + const res = await fn(client); + await client.query('COMMIT'); + return res; + } catch (e) { + try { await client.query('ROLLBACK'); } catch {} + throw e; + } finally { + client.release(); + } +} + +export async function shutdownAll() { + await Promise.all([...REGISTRY.values()].map(p => p.end())); + REGISTRY.clear(); +} diff --git a/packages/db/src/poolSingleton.mjs b/packages/db/src/poolSingleton.mjs new file mode 100644 index 0000000..35d0fae --- /dev/null +++ b/packages/db/src/poolSingleton.mjs @@ -0,0 +1,46 @@ +// Coneción Singleton a base de datos. + +import { Pool } from 'pg'; + +class Database { + constructor() { + + if (Database.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + Database.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} + +// const db = new Database(); +// db.query('SELECT * FROM users'); + +const pool = new Database(); +export default pool; +export { Database }; \ No newline at end of file diff --git a/services/app/.env.development b/services/app/.env.development index 4e75a73..4ad8890 100644 --- a/services/app/.env.development +++ b/services/app/.env.development @@ -2,37 +2,61 @@ NODE_ENV=development PORT=3030 + # ===== Session (usa el Redis del stack) ===== # Para DEV podemos reutilizar el Redis de Authentik. En prod conviene uno separado. SESSION_SECRET=Neon*Mammal*Boaster*Ludicrous*Fender8*Crablike SESSION_COOKIE_NAME=sc.sid -REDIS_URL=redis://ak-redis:6379 # ===== DB principal (metadatos de SuiteCoffee) ===== # Usa el alias de red del servicio 'db' (compose: aliases [dev-db]) DB_HOST=dev-db +DB_NAME=dev_suitecoffee_core DB_PORT=5432 -DB_NAME=dev-suitecoffee DB_USER=dev-user-suitecoffee DB_PASS=dev-pass-suitecoffee +CORE_DB_HOST=dev-db +CORE_DB_NAME=dev_suitecoffee_core +CORE_DB_PORT=5432 +CORE_DB_USER=dev-user-suitecoffee +CORE_DB_PASS=dev-pass-suitecoffee + # ===== DB tenants (Tenants de SuiteCoffee) ===== TENANTS_HOST=dev-tenants -TENANTS_DB=dev-postgres -TENANTS_USER=dev-user-postgres -TENANTS_PASS=dev-pass-postgres +TENANTS_DB=dev_suitecoffee_tenants TENANTS_PORT=5432 +TENANTS_USER=suitecoffee +TENANTS_PASS=suitecoffee -TENANT_INIT_SQL=/app/src/db/initTenant_v2.sql +TENANTS_DB_HOST=dev-tenants +TENANTS_DB_NAME=dev_suitecoffee_tenants +TENANTS_DB_PORT=5432 +TENANTS_DB_USER=suitecoffee +TENANTS_DB_PASS=suitecoffee + + +# ===== Authentik — Admin API (server-to-server dentro de la red) ===== +# Usa el alias de red del servicio 'authentik' y su puerto interno 9000 +AK_TOKEN=h2apVHbd3ApMcnnSwfQPXbvximkvP8HnUE25ot3zXWuEEtJFaNCcOzDHB6Xw +AK_REDIS_URL=redis://ak-redis:6379 + +# ===== OIDC (DEBE coincidir con el Provider) ===== +# DEV (todo dentro de la red de Docker): +# - El auth service redirige al navegador a este issuer. Si NO tenés reverse proxy hacia Authentik, +# esta URL interna NO será accesible desde el navegador del host. En ese caso, ver nota más abajo. + +APP_BASE_URL=https://suitecoffee.uy + +OIDC_LOGIN_URL=https://sso.suitecoffee.uy +OIDC_REDIRECT_URI = https://suitecoffee.uy/auth/callback OIDC_CLIEN_ID=1orMM8vOvf3WkN2FejXYvUFpPtONG0Lx1eMlwIpW OIDC_CONFIG_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/.well-known/openid-configuration OIDC_ISSUER=https://sso.suitecoffee.uy/application/o/suitecoffee/ +OIDC_ISSUER_DISCOVERY=https://sso.suitecoffee.uy/application/o/suitecoffee/.well-known/openid-configuration OIDC_AUTHORIZE_URL=https://sso.suitecoffee.uy/application/o/authorize/ OIDC_TOKEN_URL=https://sso.suitecoffee.uy/application/o/token/ OIDC_USERINFO_URL=https://sso.suitecoffee.uy/application/o/userinfo/ OIDC_LOGOUT_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/end-session/ -OIDC_JWKS_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/jwks/ - -OIDC_LOGIN_URL=https://sso.suitecoffee.uy -APP_BASE_URL=https://suitecoffee.uy +OIDC_JWKS_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/jwks/ \ No newline at end of file diff --git a/services/app/Dockerfile.production b/services/app/Dockerfile.production index 2b2dcf3..20e5cfa 100644 --- a/services/app/Dockerfile.production +++ b/services/app/Dockerfile.production @@ -1,5 +1,5 @@ # Dockerfile.dev -FROM node:22.18 +FROM node:20.19.5-bookworm # Definir variables de entorno con valores predeterminados # ARG NODE_ENV=production diff --git a/services/app/package-lock.json b/services/app/package-lock.json index 6df8869..bedd6b7 100644 --- a/services/app/package-lock.json +++ b/services/app/package-lock.json @@ -24,6 +24,7 @@ "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.2.0", "morgan": "^1.10.1", + "node-appwrite": "^20.2.1", "node-fetch": "^3.3.2", "pg": "^8.16.3", "pg-format": "^1.0.4", @@ -42,14 +43,10 @@ }, "node_modules/@ioredis/commands": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.3.1.tgz", - "integrity": "sha512-bYtU8avhGIcje3IhvF9aSjsa5URMZBHnwKtOvXsT4sfYy9gppW11gLPT/9oNqlJZD47yPKveQFTAFWpHjKvUoQ==", "license": "MIT" }, "node_modules/@redis/bloom": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.2.tgz", - "integrity": "sha512-855DR0ChetZLarblio5eM0yLwxA9Dqq50t8StXKp5bAtLT0G+rZ+eRzzqxl37sPqQKjUudSYypz55o6nNhbz0A==", "license": "MIT", "engines": { "node": ">= 18" @@ -60,8 +57,6 @@ }, "node_modules/@redis/client": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.2.tgz", - "integrity": "sha512-WtMScno3+eBpTac1Uav2zugXEoXqaU23YznwvFgkPwBQVwEHTDgOG7uEAObtZ/Nyn8SmAMbqkEubJaMOvnqdsQ==", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -72,8 +67,6 @@ }, "node_modules/@redis/json": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.2.tgz", - "integrity": "sha512-uxpVfas3I0LccBX9rIfDgJ0dBrUa3+0Gc8sEwmQQH0vHi7C1Rx1Qn8Nv1QWz5bohoeIXMICFZRcyDONvum2l/w==", "license": "MIT", "engines": { "node": ">= 18" @@ -84,8 +77,6 @@ }, "node_modules/@redis/search": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.2.tgz", - "integrity": "sha512-cNv7HlgayavCBXqPXgaS97DRPVWFznuzsAmmuemi2TMCx5scwLiP50TeZvUS06h/MG96YNPe6A0Zt57yayfxwA==", "license": "MIT", "engines": { "node": ">= 18" @@ -96,8 +87,6 @@ }, "node_modules/@redis/time-series": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.2.tgz", - "integrity": "sha512-g2NlHM07fK8H4k+613NBsk3y70R2JIM2dPMSkhIjl2Z17SYvaYKdusz85d7VYOrZBWtDrHV/WD2E3vGu+zni8A==", "license": "MIT", "engines": { "node": ">= 18" @@ -108,8 +97,6 @@ }, "node_modules/@types/body-parser": { "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "license": "MIT", "dependencies": { "@types/connect": "*", @@ -118,8 +105,6 @@ }, "node_modules/@types/connect": { "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -127,8 +112,6 @@ }, "node_modules/@types/express": { "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "license": "MIT", "dependencies": { "@types/body-parser": "*", @@ -139,8 +122,6 @@ }, "node_modules/@types/express-serve-static-core": { "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -151,14 +132,10 @@ }, "node_modules/@types/http-errors": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "license": "MIT" }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { "@types/ms": "*", @@ -167,20 +144,14 @@ }, "node_modules/@types/mime": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "license": "MIT" }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { "version": "24.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", - "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", "license": "MIT", "dependencies": { "undici-types": "~7.10.0" @@ -188,20 +159,14 @@ }, "node_modules/@types/qs": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -210,8 +175,6 @@ }, "node_modules/@types/serve-static": { "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -252,8 +215,6 @@ }, "node_modules/basic-auth": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "license": "MIT", "dependencies": { "safe-buffer": "5.1.2" @@ -264,14 +225,10 @@ }, "node_modules/basic-auth/node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, "node_modules/bcrypt": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -333,8 +290,6 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, "node_modules/bytes": { @@ -404,8 +359,6 @@ }, "node_modules/cluster-key-slot": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "license": "Apache-2.0", "engines": { "node": ">=0.10.0" @@ -418,8 +371,6 @@ }, "node_modules/connect-redis": { "version": "9.0.0", - "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-9.0.0.tgz", - "integrity": "sha512-QwzyvUePTMvEzG1hy45gZYw3X3YHrjmEdSkayURlcZft7hqadQ3X39wYkmCqblK2rGlw+XItELYt6GnyG6DEIQ==", "license": "MIT", "engines": { "node": ">=18" @@ -448,6 +399,8 @@ }, "node_modules/cookie": { "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -521,8 +474,6 @@ }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "license": "MIT", "engines": { "node": ">= 12" @@ -545,8 +496,6 @@ }, "node_modules/denque": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "license": "Apache-2.0", "engines": { "node": ">=0.10" @@ -583,8 +532,6 @@ }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -694,8 +641,6 @@ }, "node_modules/express-session": { "version": "1.18.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "license": "MIT", "dependencies": { "cookie": "0.7.2", @@ -713,14 +658,10 @@ }, "node_modules/express-session/node_modules/cookie-signature": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -728,14 +669,10 @@ }, "node_modules/express-session/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { "type": "github", @@ -807,8 +744,6 @@ }, "node_modules/formdata-polyfill": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -962,8 +897,6 @@ }, "node_modules/ioredis": { "version": "5.7.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.7.0.tgz", - "integrity": "sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==", "license": "MIT", "dependencies": { "@ioredis/commands": "^1.3.0", @@ -1055,8 +988,6 @@ }, "node_modules/jose": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz", - "integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -1064,8 +995,6 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "license": "MIT", "dependencies": { "jws": "^3.2.2", @@ -1086,8 +1015,6 @@ }, "node_modules/jwa": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -1097,8 +1024,6 @@ }, "node_modules/jwks-rsa": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", - "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", "license": "MIT", "dependencies": { "@types/express": "^4.17.20", @@ -1114,8 +1039,6 @@ }, "node_modules/jwks-rsa/node_modules/jose": { "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -1123,8 +1046,6 @@ }, "node_modules/jws": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "license": "MIT", "dependencies": { "jwa": "^1.4.1", @@ -1132,74 +1053,50 @@ } }, "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + "version": "1.1.5" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "license": "MIT" }, "node_modules/lodash.defaults": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, "node_modules/lodash.isarguments": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -1210,8 +1107,6 @@ }, "node_modules/lru-memoizer": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", - "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", "license": "MIT", "dependencies": { "lodash.clonedeep": "^4.5.0", @@ -1272,8 +1167,6 @@ }, "node_modules/morgan": { "version": "1.10.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", - "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", @@ -1288,8 +1181,6 @@ }, "node_modules/morgan/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -1297,14 +1188,10 @@ }, "node_modules/morgan/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -1326,18 +1213,22 @@ }, "node_modules/node-addon-api": { "version": "8.5.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" } }, + "node_modules/node-appwrite": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-20.2.1.tgz", + "integrity": "sha512-RweIh+3RHjprsxhWaJzcQr/UDMBMsZCma50TIJ9t3onVgs5jAT9aqFnsMlaaC9QZn1sXpPUQV90W6uvtm64DnQ==", + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-native-with-agent": "1.7.2" + } + }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -1355,8 +1246,6 @@ }, "node_modules/node-fetch": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -1371,10 +1260,14 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-fetch-native-with-agent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-fetch-native-with-agent/-/node-fetch-native-with-agent-1.7.2.tgz", + "integrity": "sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g==", + "license": "MIT" + }, "node_modules/node-gyp-build": { "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "license": "MIT", "bin": { "node-gyp-build": "bin.js", @@ -1446,8 +1339,6 @@ }, "node_modules/on-headers": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1484,6 +1375,8 @@ }, "node_modules/pg": { "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", "dependencies": { "pg-connection-string": "^2.9.1", @@ -1509,11 +1402,15 @@ }, "node_modules/pg-cloudflare": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", "license": "MIT" }, "node_modules/pg-format": { @@ -1525,6 +1422,8 @@ }, "node_modules/pg-int8": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", "license": "ISC", "engines": { "node": ">=4.0.0" @@ -1532,6 +1431,8 @@ }, "node_modules/pg-pool": { "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" @@ -1539,10 +1440,14 @@ }, "node_modules/pg-protocol": { "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", "license": "MIT" }, "node_modules/pg-types": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", "license": "MIT", "dependencies": { "pg-int8": "1.0.1", @@ -1557,6 +1462,8 @@ }, "node_modules/pgpass": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", "license": "MIT", "dependencies": { "split2": "^4.1.0" @@ -1579,6 +1486,8 @@ }, "node_modules/postgres-array": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", "license": "MIT", "engines": { "node": ">=4" @@ -1586,6 +1495,8 @@ }, "node_modules/postgres-bytea": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1593,6 +1504,8 @@ }, "node_modules/postgres-date": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1600,6 +1513,8 @@ }, "node_modules/postgres-interval": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "license": "MIT", "dependencies": { "xtend": "^4.0.0" @@ -1639,8 +1554,6 @@ }, "node_modules/random-bytes": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1679,8 +1592,6 @@ }, "node_modules/redis": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-5.8.2.tgz", - "integrity": "sha512-31vunZj07++Y1vcFGcnNWEf5jPoTkGARgfWI4+Tk55vdwHxhAvug8VEtW7Cx+/h47NuJTEg/JL77zAwC6E0OeA==", "license": "MIT", "dependencies": { "@redis/bloom": "5.8.2", @@ -1695,8 +1606,6 @@ }, "node_modules/redis-errors": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", "license": "MIT", "engines": { "node": ">=4" @@ -1704,8 +1613,6 @@ }, "node_modules/redis-parser": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "license": "MIT", "dependencies": { "redis-errors": "^1.0.0" @@ -1914,6 +1821,8 @@ }, "node_modules/split2": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "license": "ISC", "engines": { "node": ">= 10.x" @@ -1921,8 +1830,6 @@ }, "node_modules/standard-as-callback": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", "license": "MIT" }, "node_modules/statuses": { @@ -1983,8 +1890,6 @@ }, "node_modules/uid-safe": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "license": "MIT", "dependencies": { "random-bytes": "~1.0.0" @@ -2000,8 +1905,6 @@ }, "node_modules/undici-types": { "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "license": "MIT" }, "node_modules/unpipe": { @@ -2020,8 +1923,6 @@ }, "node_modules/web-streams-polyfill": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "license": "MIT", "engines": { "node": ">= 8" @@ -2047,6 +1948,8 @@ }, "node_modules/xtend": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "license": "MIT", "engines": { "node": ">=0.4" @@ -2054,8 +1957,6 @@ }, "node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" } } diff --git a/services/app/package.json b/services/app/package.json index 9ae6e52..6b65566 100644 --- a/services/app/package.json +++ b/services/app/package.json @@ -1,11 +1,11 @@ { "name": "aplication", "version": "1.0.0", - "main": "src/index.js", + "main": "src/index.mjs", "scripts": { - "start": "NODE_ENV=production node ./src/index.js", - "dev": "NODE_ENV=development npx nodemon ./src/index.js", - "test": "NODE_ENV=stage node ./src/index.js" + "start": "NODE_ENV=production node ./src/index.mjs", + "dev": "NODE_ENV=development npx nodemon ./src/index.mjs", + "test": "NODE_ENV=stage node ./src/index.mjs" }, "author": "Mateo Saldain", "license": "ISC", @@ -30,12 +30,18 @@ "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.2.0", "morgan": "^1.10.1", + "node-appwrite": "^20.2.1", "node-fetch": "^3.3.2", "pg": "^8.16.3", "pg-format": "^1.0.4", "redis": "^5.8.2", "serve-favicon": "^2.5.1" }, - "keywords": [], + "imports": { + "#v1Router": "./src/api/v1/routes/routes.js", + "#pages": "./src/pages/pages.js", + "#db": "./src/db/poolSingleton.js" + }, + "keywords": [], "description": "" } diff --git a/services/app/src/api/v1/routes/routes.js b/services/app/src/api/v1/routes/routes.js new file mode 100644 index 0000000..c17f2cc --- /dev/null +++ b/services/app/src/api/v1/routes/routes.js @@ -0,0 +1,340 @@ +// services/manso/src/api/v1/routes/routes.js + +import { Router } from 'express'; +import pool from '#db'; // Pool Singleton +const router = Router(); + +// ========================================================== +// Rutas de API v1 +// ========================================================== + + + +// ---------------------------------------------------------- +// API Comandas +// ---------------------------------------------------------- + +router.route('/comandas').get( async (req, res, next) => { + try { + var client = await pool.getClient() + const estado = (req.query.estado || '').trim() || null; + const limit = Math.min(parseInt(req.query.limit || '200', 10), 1000); + + const { rows } = await client.query( + `SELECT * FROM public.f_comandas_resumen($1, $2)`, + [estado, limit] + ); + res.json(rows); + } catch (e) { + next(e); + } finally { + client.release(); + } +}); + +router.route('/comandas/:id/detalle').get( async (req, res, next) => { + try { + const client = await pool.getClient() + client.query( + `SELECT id_det_comanda, id_producto, producto_nombre, + cantidad, pre_unitario, subtotal, observaciones + FROM public.v_comandas_detalle_items + WHERE id_comanda = $1::int + ORDER BY id_det_comanda`, + [req.params.id] + ) + .then(r => res.json(r.rows)) + .catch(next) + client.release(); + } catch (error) { + next(e); + } +}); + +router.route('/comandas/:id/cerrar').post( async (req, res, next) => { + try { + const client = await pool.getClient() + const id = Number(req.params.id); + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: 'id inválido' }); + } + const { rows } = await client.query( + `SELECT public.f_cerrar_comanda($1) AS data`, + [id] + ); + if (!rows.length || rows[0].data === null) { + return res.status(404).json({ error: 'Comanda no encontrada' }); + } + res.json(rows[0].data); + client.release(); + } catch (err) { next(err); } +}); + +router.route('/comandas/:id/abrir').post( async (req, res, next) => { + try { + const client = await pool.getClient() + const id = Number(req.params.id); + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: 'id inválido' }); + } + const { rows } = await client.query( + `SELECT public.f_abrir_comanda($1) AS data`, + [id] + ); + if (!rows.length || rows[0].data === null) { + return res.status(404).json({ error: 'Comanda no encontrada' }); + } + res.json(rows[0].data); + client.release(); + } catch (err) { next(err); } +}); + + + +// ---------------------------------------------------------- +// API Productos +// ---------------------------------------------------------- + +// GET producto + receta +router.route('/rpc/get_producto/:id').get( async (req, res) => { + const client = await pool.getClient() + const id = Number(req.params.id); + const { rows } = await client.query('SELECT public.get_producto($1) AS data', [id]); + res.json(rows[0]?.data || {}); + client.release(); +}); + +// POST guardar producto + receta +router.route('/rpc/save_producto').post(async (req, res) => { + try { + // console.debug('receta payload:', req.body?.receta); // habilitalo si lo necesitás + const client = await pool.getClient() + const q = 'SELECT public.save_producto($1,$2,$3,$4,$5,$6,$7::jsonb) AS id_producto'; + const { id_producto=null, nombre, img_producto=null, precio=0, activo=true, id_categoria=null, receta=[] } = req.body || {}; + const params = [id_producto, nombre, img_producto, precio, activo, id_categoria, JSON.stringify(receta||[])]; + const { rows } = await client.query(q, params); + res.json(rows[0] || {}); + client.release(); + } catch(e) { + console.error(e); + res.status(500).json({ error: 'save_producto failed' }); + } +}); + + + +// ---------------------------------------------------------- +// API Materias Primas +// ---------------------------------------------------------- + +// GET MP + proveedores +router.route('/rpc/get_materia/:id').get(async (req, res) => { + const id = Number(req.params.id); + try { + const client = await pool.getClient() + const { rows } = await client.query('SELECT public.get_materia_prima($1) AS data', [id]); + res.json(rows[0]?.data || {}); + client.release(); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'get_materia failed' }); + } +}); + +// SAVE MP + proveedores (array) +router.route('/rpc/save_materia').post( async (req, res) => { + const { id_mat_prima=null, nombre, unidad, activo=true, proveedores=[] } = req.body || {}; + try { + const q = 'SELECT public.save_materia_prima($1,$2,$3,$4,$5::jsonb) AS id_mat_prima'; + const params = [id_mat_prima, nombre, unidad, activo, JSON.stringify(proveedores||[])]; + const { rows } = await pool.query(q, params); + res.json(rows[0] || {}); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'save_materia failed' }); + } +}); + + + +// ---------------------------------------------------------- +// API Usuarios y Asistencias +// ---------------------------------------------------------- + +// POST /api/rpc/find_usuarios_por_documentos { docs: ["12345678","09123456", ...] } +router.route('/rpc/find_usuarios_por_documentos').post( async (req, res) => { + try { + const docs = Array.isArray(req.body?.docs) ? req.body.docs : []; + const sql = 'SELECT public.find_usuarios_por_documentos($1::jsonb) AS data'; + const { rows } = await pool.query(sql, [JSON.stringify(docs)]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'find_usuarios_por_documentos failed' }); + } +}); + +// POST /api/rpc/import_asistencia { registros: [...], origen?: "AGL_001.txt" } +router.route('/rpc/import_asistencia').post( async (req, res) => { + try { + const registros = Array.isArray(req.body?.registros) ? req.body.registros : []; + const origen = req.body?.origen || null; + const sql = 'SELECT public.import_asistencia($1::jsonb,$2) AS data'; + const { rows } = await pool.query(sql, [JSON.stringify(registros), origen]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'import_asistencia failed' }); + } +}); + +// Consultar datos de asistencia (raw + pares) para un usuario y rango +router.route('/rpc/asistencia_get').post( async (req, res) => { + try { + const { doc, desde, hasta } = req.body || {}; + const sql = 'SELECT public.asistencia_get($1::text,$2::date,$3::date) AS data'; + const { rows } = await pool.query(sql, [doc, desde, hasta]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'asistencia_get failed' }); + } +}); + +// Editar un registro crudo y recalcular pares +router.route('/rpc/asistencia_update_raw').post( async (req, res) => { + try { + const { id_raw, fecha, hora, modo } = req.body || {}; + const sql = 'SELECT public.asistencia_update_raw($1::bigint,$2::date,$3::text,$4::text) AS data'; + const { rows } = await pool.query(sql, [id_raw, fecha, hora, modo ?? null]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'asistencia_update_raw failed' }); + } +}); + +// Eliminar un registro crudo y recalcular pares +router.route('/rpc/asistencia_delete_raw').post( async (req, res) => { + try { + const { id_raw } = req.body || {}; + const sql = 'SELECT public.asistencia_delete_raw($1::bigint) AS data'; + const { rows } = await pool.query(sql, [id_raw]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'asistencia_delete_raw failed' }); + } +}); + + +// ---------------------------------------------------------- +// API Reportes +// ---------------------------------------------------------- + +// POST /api/rpc/report_tickets { year } +router.route('/rpc/report_tickets').post( async (req, res) => { + try { + const y = parseInt(req.body?.year ?? req.query?.year, 10); + const year = (Number.isFinite(y) && y >= 2000 && y <= 2100) + ? y + : (new Date()).getFullYear(); + + const { rows } = await pool.query( + 'SELECT public.report_tickets_year($1::int) AS j', [year] + ); + res.json(rows[0].j); + } catch (e) { + console.error('report_tickets error:', e); + res.status(500).json({ + error: 'report_tickets failed', + message: e.message, detail: e.detail, where: e.where, code: e.code + }); + } +}); + +// POST /api/rpc/report_asistencia { desde: 'YYYY-MM-DD', hasta: 'YYYY-MM-DD' } +router.route('/rpc/report_asistencia').post( async (req, res) => { + try { + let { desde, hasta } = req.body || {}; + // defaults si vienen vacíos/invalidos + const re = /^\d{4}-\d{2}-\d{2}$/; + if (!re.test(desde) || !re.test(hasta)) { + const end = new Date(); + const start = new Date(end); start.setDate(end.getDate()-30); + desde = start.toISOString().slice(0,10); + hasta = end.toISOString().slice(0,10); + } + + const { rows } = await pool.query( + 'SELECT public.report_asistencia($1::date,$2::date) AS j', [desde, hasta] + ); + res.json(rows[0].j); + } catch (e) { + console.error('report_asistencia error:', e); + res.status(500).json({ + error: 'report_asistencia failed', + message: e.message, detail: e.detail, where: e.where, code: e.code + }); + } +}); + +// ---------------------------------------------------------- +// API Compras y Gastos +// ---------------------------------------------------------- + +// Guardar (insert/update) +router.route('/rpc/save_compra').post( async (req, res) => { + try { + const { id_compra, id_proveedor, fec_compra, detalles } = req.body || {}; + const sql = 'SELECT * FROM public.save_compra($1::int,$2::int,$3::timestamptz,$4::jsonb)'; + const args = [id_compra ?? null, id_proveedor, fec_compra ? new Date(fec_compra) : null, JSON.stringify(detalles)]; + const { rows } = await pool.query(sql, args); + res.json(rows[0]); // { id_compra, total } + } catch (e) { + console.error('save_compra error:', e); + res.status(500).json({ error: 'save_compra failed', message: e.message, detail: e.detail, where: e.where, code: e.code }); + } +}); + + +// Obtener para editar +router.route('/rpc/get_compra').post( async (req, res) => { + try { + const { id_compra } = req.body || {}; + const sql = `SELECT public.get_compra($1::int) AS data`; + const { rows } = await pool.query(sql, [id_compra]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'get_compra failed' }); + } +}); + +// Eliminar +router.route('/rpc/delete_compra').post( async (req, res) => { + try { + const { id_compra } = req.body || {}; + await pool.query(`SELECT public.delete_compra($1::int)`, [id_compra]); + res.json({ ok: true }); + } catch (e) { + console.error(e); res.status(500).json({ error: 'delete_compra failed' }); + } +}); + + +// POST /api/rpc/report_gastos { year: 2025 } +router.route('/rpc/report_gastos').post( async (req, res) => { + try { + const year = parseInt(req.body?.year ?? new Date().getFullYear(), 10); + const { rows } = await pool.query( + 'SELECT public.report_gastos($1::int) AS j', [year] + ); + res.json(rows[0].j); + } catch (e) { + console.error('report_gastos error:', e); + res.status(500).json({ + error: 'report_gastos failed', + message: e.message, detail: e.detail, code: e.code + }); + } +}); + + +export default router; \ No newline at end of file diff --git a/services/app/src/db/poolSingleton.js b/services/app/src/db/poolSingleton.js new file mode 100644 index 0000000..a0935c2 --- /dev/null +++ b/services/app/src/db/poolSingleton.js @@ -0,0 +1,83 @@ +// Coneción Singleton a base de datos. + +import { Pool } from 'pg'; + +class DatabaseCore { + constructor() { + + if (DatabaseCore.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + DatabaseCore.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} +class DatabaseTenants { + constructor() { + + if (DatabaseTenants.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + DatabaseTenants.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} + +// const db = new Database(); +// db.query('SELECT * FROM users'); + +const poolCore = new DatabaseCore(); +const poolTenants = new DatabaseTenants(); +export default {poolCore, poolTenants}; +export { poolCore, poolTenants }; +//export { DatabaseCore, DatabaseTenants }; \ No newline at end of file diff --git a/services/app/src/index.js b/services/app/src/index.js deleted file mode 100644 index 174a4af..0000000 --- a/services/app/src/index.js +++ /dev/null @@ -1,377 +0,0 @@ -// // services/app/src/index.js -// // ------------------------------------------------------------ -// // SuiteCoffee — Servicio APP (UI + APIs negocio) -// // - Vistas EJS en ./views (dashboard.ejs, comandas.ejs, etc.) -// // - Sesión compartida con AUTH (cookie: sc.sid, Redis) -// // ------------------------------------------------------------ -import 'dotenv/config'; // Variables de entorno directamente -// import dotenv from 'dotenv'; -import favicon from 'serve-favicon'; // Favicon - -import cors from 'cors'; // Seguridad en solicitudes de orige -import { Pool } from 'pg'; // Controlador node-postgres -import path from 'node:path'; // Rutas del servidor -import { fileURLToPath } from 'url'; // Converts a file:// URL string or URL object into a platform-specific file - -import expressLayouts from 'express-ejs-layouts'; -import express from 'express'; // Framework para enderizado de apps Web -import { jwtVerify, createRemoteJWKSet } from "jose"; - -import cookieParser from 'cookie-parser'; - -import { loadColumns, loadForeignKeys, loadPrimaryKey, pickLabelColumn } from "./utilities/cargaEnVista.js"; - -import { createRedisSession } from "../shared/middlewares/redisConnect.js"; -// // ---------------------------------------------------------- -// // Utilidades -// // ---------------------------------------------------------- - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -// const url = v => !v ? "" : (v.startsWith("http") ? v : `/img/productos/${v}`); -// const VALID_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/; - -// // Identificadores SQL -> comillas dobles y escape correcto -// const q = (s) => `"${String(s).replace(/"/g, '""')}"`; -// const qi = (ident) => `"${String(ident).replace(/"/g, '""')}"`; -// const CLEAN_HEX = (s) => (String(s || '').toLowerCase().replace(/[^0-9a-f]/g, '') || null); - -// Función para verificar que ciertas variables de entorno estén definida -function checkRequiredEnvVars(...requiredKeys) { - const missingKeys = requiredKeys.filter((key) => !process.env[key]); // Filtramos las que NO existen en process.env - if (missingKeys.length > 0) { // Si falta alguna, mostramos una advertencia - console.warn( - `[APP] No se encontraron las siguientes variables de entorno: ${missingKeys.join(', ')}` - ); - } -} - -// ¿Está permitida la tabla? -function ensureTable(name) { - const t = String(name || '').toLowerCase(); - if (!ALLOWED_TABLES.includes(t)) throw new Error(`Tabla ${t} no permitida`); - return t; -} - -// -async function getClient() { - const client = await mainPool.connect(); - return client; -} - -// ----------------------------------------------------------------------------- -// Validación de entorno mínimo (ajusta nombres si difieren) -// ----------------------------------------------------------------------------- -checkRequiredEnvVars( - // Sesión - 'SESSION_SECRET', 'REDIS_URL', - // DB principal - 'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS', - // DB de tenants - 'TENANTS_HOST', 'TENANTS_DB', 'TENANTS_USER', 'TENANTS_PASS' -); - -// ---------------------------------------------------------- -// App -// ---------------------------------------------------------- -const app = express(); -app.set('trust proxy', Number(process.env.TRUST_PROXY_HOPS || 2)); -app.disable("x-powered-by"); -app.use(cors({ origin: true, credentials: true })); -app.use(express.json()); -app.use(express.json({ limit: '1mb' })); -app.use(express.urlencoded({ extended: true })); -app.use(express.static(path.join(__dirname, 'pages'))); - - -// ---------------------------------------------------------- -// Motor de vistas EJS -// ---------------------------------------------------------- -app.set("views", path.join(__dirname, "views")); -app.set("view engine", "ejs"); -app.set("layout", "layouts/main"); -app.use(expressLayouts); -app.use(cookieParser(process.env.SESSION_SECRET)); - -// Archivos estáticos que fuerzan la re-descarga de arhivos -app.use(express.static(path.join(__dirname, "public"), { - etag: false, maxAge: 0, - setHeaders: (res, path) => { - res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); - } -})); - -app.use('/favicon', express.static(path.join(__dirname, 'public', 'favicon'), { maxAge: '1y' })); -app.use(favicon(path.join(__dirname, 'public', 'favicon', 'favicon.ico'), { maxAge: '1y' })); - -// ---------------------------------------------------------- -// Middleware para datos globales -// ---------------------------------------------------------- -app.use((req, res, next) => { - res.locals.currentPath = req.path; - res.locals.pageTitle = "SuiteCoffee"; - res.locals.pageId = ""; - next(); -}); - -// ---------------------------------------------------------- -// Configuración de Pool principal a PostgreSQL -// ---------------------------------------------------------- -const mainPool = new Pool({ - host: process.env.DB_HOST || '', - database: process.env.DB_NAME || '', - port: Number(process.env.DB_PORT || 5432), - user: process.env.DB_USER || '', - password: process.env.DB_PASS || '', - // ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, - max: -1, - idleTimeoutMillis: 30_000, -}); - -// ---------------------------------------------------------- -// Configuración Pool de Tenants a PostgreSQL -// ---------------------------------------------------------- -const tenantsPool = new Pool({ - host: process.env.TENANTS_HOST, - database: process.env.TENANTS_DB, - port: Number(process.env.TENANTS_PORT || 5432), - user: process.env.TENANTS_USER, - password: process.env.TENANTS_PASS, - max: -1, - idleTimeoutMillis: 30_000, -}); - -// ---------------------------------------------------------- -// Seguridad: Tablas permitidas -// ---------------------------------------------------------- - -// const ALLOWED_TABLES = [ -// 'roles', 'usuarios', 'usua_roles', -// 'categorias', 'productos', -// 'clientes', 'mesas', -// 'comandas', 'deta_comandas', -// 'proveedores', 'compras', 'deta_comp_producto', -// 'mate_primas', 'deta_comp_materias', -// 'prov_producto', 'prov_mate_prima', -// 'receta_producto', 'asistencia_resumen_diario', -// 'asistencia_intervalo', 'vw_compras' -// ]; - - -// ----------------------------------------------------------------------------- -// Sesión (Redis) — misma cookie que AUTH -// ----------------------------------------------------------------------------- - - const PORT = process.env.PORT || 3030; - const ISSUER = process.env.AUTHENTIK_ISSUER?.replace(/\/?$/, "/"); - const CLIENT_ID = process.env.OIDC_CLIENT_ID; - const SSO_ENTRY_URL = process.env.SSO_ENTRY_URL || "https://sso.suitecoffee.uy"; - - // 1) SIEMPRE montar sesión ANTES de las rutas - const { sessionMw, trustProxy } = await createRedisSession(); - - app.use(sessionMw); - - const JWKS = createRemoteJWKSet(new URL(`${ISSUER}jwks/`)); - - async function verifyIdToken(idToken) { - const { payload } = await jwtVerify(idToken, JWKS, { - issuer: ISSUER.replace(/\/$/, ""), - audience: CLIENT_ID, - }); - return payload; - } - - function requireToken(req, res, next) { - const id = req.session?.tokens?.id_token; // <- defensivo - if (!id) return res.redirect(302, SSO_ENTRY_URL); - next(); - } - - app.get("/", requireToken, async (req, res) => { - try { - const idToken = req.session?.tokens?.id_token; // <- defensivo - if (!idToken) return res.redirect(302, SSO_ENTRY_URL); - const claims = await verifyIdToken(idToken); - const email = claims.email || claims.preferred_username || "sin-email"; - res.json({ usuario: { email, sub: claims.sub } }); - } catch (e) { - console.error("/ verificación ID token", e); - res.redirect(302, SSO_ENTRY_URL); - } - }); - - -// // ----------------------------------------------------------------------------- -// // Comprobaciones de tenants en DB principal -// // ----------------------------------------------------------------------------- - - - - - - -// // Abre un client al DB de tenants y fija search_path al esquema del usuario -// async function withTenant(req, res, next) { -// try { -// const hex = CLEAN_HEX(req.session?.user?.tenant_uuid); -// if (!hex) return res.status(400).json({ error: 'tenant-missing' }); - -// const schema = `schema_tenant_${hex}`; -// const client = await tenantsPool.connect(); - -// // Fijar search_path para que las consultas apunten al esquema del tenant -// await client.query(`SET SESSION search_path TO ${qi(schema)}, public`); - -// // Hacemos el client accesible para los handlers de routes.legacy.js -// req.pg = client; - -// // Liberar el client al finalizar la respuesta -// const release = () => { -// try { client.release(); } catch {} -// }; -// res.on('finish', release); -// res.on('close', release); - -// next(); -// } catch (e) { -// next(e); -// } -// } - - - -// // ---------------------------------------------------------- -// // Rutas de UI -// // ---------------------------------------------------------- - - -// app.get("/inicio", (req, res) => { -// try { -// const safeUser = req.session?.user || null; -// const safeCookies = req.cookies || {}; -// const safeSession = req.session ? JSON.parse(JSON.stringify(req.session)) : {}; -// res.locals.pageTitle = "Inicio"; -// res.locals.pageId = "inicio"; // <- importante -// return res.render('inicio', { -// user: safeUser, -// cookies: safeCookies, -// session: safeSession, -// }); -// } catch (e) { -// next(e); -// } -// }); - -// app.get("/dashboard", requireToken,(req, res) => { -// res.locals.pageTitle = "Dashboard"; -// res.locals.pageId = "dashboard"; // <- importante -// res.render("dashboard"); -// }); - -// app.get("/comandas", requireToken,(req, res) => { -// res.locals.pageTitle = "Comandas"; -// res.locals.pageId = "comandas"; // <- importante para el sidebar contextual -// res.render("comandas"); -// }); - -// app.get("/estadoComandas", requireToken,(req, res) => { -// res.locals.pageTitle = "Estado de Comandas"; -// res.locals.pageId = "estadoComandas"; -// res.render("estadoComandas"); -// }); - -// app.get("/productos", requireToken,(req, res) => { -// res.locals.pageTitle = "Productos"; -// res.locals.pageId = "productos"; -// res.render("productos"); -// }); - -// app.get('/usuarios', requireToken,(req, res) => { -// res.locals.pageTitle = 'Usuarios'; -// res.locals.pageId = 'usuarios'; -// res.render('usuarios'); -// }); - -// app.get('/reportes', requireToken,(req, res) => { -// res.locals.pageTitle = 'Reportes'; -// res.locals.pageId = 'reportes'; -// res.render('reportes'); -// }); - -// app.get('/compras', requireToken,(req, res) => { -// res.locals.pageTitle = 'Compras'; -// res.locals.pageId = 'compras'; -// res.render('compras'); -// }); - -// // Página para definir contraseña (el form envía al servicio AUTH) -// app.get('/set-password', (req, res) => { -// const pp = req.session?.pendingPassword; -// if (!pp) return req.session?.user ? res.redirect('/') : res.redirect('https://sso.suitecoffee.uy/if/flow/default-authentication-flow/'); - -// res.type('html').send(` -// -// SuiteCoffee · Definir contraseña -// -//
-//

Definir contraseña

-//
-// -// -// -// Luego te redirigiremos a iniciar sesión por SSO. -//
-//
-// `); -// }); - - - - - - - - - - -// ---------------------------------------------------------- -// Verificación de conexión -// ---------------------------------------------------------- -async function verificarConexion() { - try { - console.log(`[APP] Comprobando accesibilidad a la db ${process.env.DB_NAME} del host ${process.env.DB_HOST} ...`); - const client = await mainPool.connect(); - const { rows } = await client.query('SELECT NOW() AS ahora'); - console.log(`\n[APP] Conexión con ${process.env.DB_NAME} OK. Hora DB:`, rows[0].ahora); - client.release(); - } catch (error) { - console.error('[APP] Error al conectar con la base de datos al iniciar:', error.message); - console.error('[APP] Revisar DB_HOST/USER/PASS/NAME, accesos de red y firewall.'); - } -} - -// // ----------------------------------------------------------------------------- -// // Healthcheck -// ----------------------------------------------------------------------------- -app.get('/health', (_req, res) => res.status(200).json({ status: 'ok'})); - -// ----------------------------------------------------------------------------- -// 404 + Manejo de errores -// ----------------------------------------------------------------------------- -app.use((req, res) => res.status(404).json({ error: 'Error 404, No se encontró la página', path: req.originalUrl })); - -app.use((err, _req, res, _next) => { - console.error('[APP] ', err); - if (res.headersSent) return; - res.status(500).json({ error: '¡Oh! A ocurrido un error en el servidor app.', detail: err.stack || String(err) }); -}); - -// ---------------------------------------------------------- -// Inicio del servidor -// ---------------------------------------------------------- -app.listen(3030, () => { - console.log(`[APP] SuiteCoffee corriendo en http://localhost:${3030}`); - verificarConexion(); -}); - diff --git a/services/app/src/index.mjs b/services/app/src/index.mjs new file mode 100644 index 0000000..26f334e --- /dev/null +++ b/services/app/src/index.mjs @@ -0,0 +1,437 @@ +// services/app/src/index.js +// ------------------------------------------------------------ +// SuiteCoffee — Aplicación Principal (Express) +// ------------------------------------------------------------ + +import 'dotenv/config'; +import favicon from 'serve-favicon'; // Favicon +import express from 'express'; // Framework para enderizado de apps Web +import expressLayouts from 'express-ejs-layouts'; +// import { poolCore, poolTenants } from '@suitecoffee/db'; // dbCore y dbTenants desde módulo +import { poolCore, poolTenants } from '#db'; // dbCore y dbTenants +import v1Router from '#v1Router'; // Rutas API v1 +import expressPages from '#pages'; // Rutas "/", "/dashboard", ... + + +import path from 'path'; +import { fileURLToPath } from 'url'; // Converts a file:// URL string or URL object into a platform-specific file +import cookieParser from 'cookie-parser'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + + + + + + + + +// ----------------------------------------------------------------------------- +// Validación de entorno mínimo (ajusta nombres si difieren) +// ----------------------------------------------------------------------------- + +// Función para verificar que ciertas variables de entorno estén definida +function checkRequiredEnvVars(...requiredKeys) { + const missingKeys = requiredKeys.filter((key) => !process.env[key]); // Filtramos las que NO existen en process.env + if (missingKeys.length > 0) { // Si falta alguna, mostramos una advertencia + console.warn( + `[APP] No se encontraron las siguientes variables de entorno: \n\n-> ${missingKeys.join('\n-> ')}`+ + `\n` + ); + } +} + +checkRequiredEnvVars( + 'PORT', 'APP_BASE_URL', + 'CORE_DB_HOST', 'CORE_DB_PORT', 'CORE_DB_NAME', + 'TENANTS_DB_HOST', 'TENANTS_DB_PORT', 'TENANTS_DB_NAME', + + 'OIDC_LOGIN_URL', 'OIDC_REDIRECT_URI', + 'OIDC_CLIEN_ID', 'OIDC_CONFIG_URL', 'OIDC_ISSUER', + 'OIDC_ISSUER_DISCOVERY', 'OIDC_AUTHORIZE_URL', 'OIDC_TOKEN_URL', + 'OIDC_USERINFO_URL', 'OIDC_LOGOUT_URL', 'OIDC_JWKS_URL', + + 'SESSION_SECRET', 'SESSION_COOKIE_NAME', + 'AK_REDIS_URL', 'AK_TOKEN' +); + + + + + +// ---------------------------------------------------------- +// Variables del sistema +// ---------------------------------------------------------- + +// De entorno +const PORT = process.env.PORT; +const APP_BASE_URL = process.env.APP_BASE_URL; + +const CORE_DB_HOST = process.env.CORE_DB_HOST; +const CORE_DB_PORT = process.env.CORE_DB_PORT; +const CORE_DB_NAME = process.env.CORE_DB_NAME; + +const TENANTS_DB_HOST = process.env.TENANTS_DB_HOST; +const TENANTS_DB_PORT = process.env.TENANTS_DB_PORT; +const TENANTS_DB_NAME = process.env.TENANTS_DB_NAME; + +const OIDC_LOGIN_URL = process.env.OIDC_LOGIN_URL; +const OIDC_REDIRECT_URI = process.env.OIDC_REDIRECT_URI; + +const OIDC_CLIEN_ID = process.env.OIDC_CLIEN_ID; +const OIDC_CONFIG_URL = process.env.OIDC_CONFIG_URL; +const OIDC_ISSUER = process.env.OIDC_ISSUER; +const OIDC_ISSUER_DISCOVERY = process.env.OIDC_ISSUER_DISCOVERY; +const OIDC_AUTHORIZE_URL = process.env.OIDC_AUTHORIZE_URL; +const OIDC_TOKEN_URL = process.env.OIDC_TOKEN_URL; +const OIDC_USERINFO_URL = process.env.OIDC_USERINFO_URL; +const OIDC_LOGOUT_URL = process.env.OIDC_LOGOUT_URL; +const OIDC_JWKS_URL = process.env.OIDC_JWKS_URL; + +const AK_SESSION_SECRET = process.env.AK_SESSION_SECRET; +const AK_SESSION_COOKIE_NAME = process.env.AK_SESSION_COOKIE_NAME; +const AK_REDIS_URL = process.env.AK_REDIS_URL; + + + +const url = v => !v ? "" : (v.startsWith("http") ? v : `/img/productos/${v}`); +const VALID_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/; +const q = (s) => `"${String(s).replace(/"/g, '""')}"`; // Identificadores SQL -> comillas dobles y escape correcto + + + +// ---------------------------------------------------------- +// App + Motor de vistas EJS +// ---------------------------------------------------------- + +const app = express(); +app.set('trust proxy', true); +app.set("views", path.join(__dirname, "views")); +app.set("view engine", "ejs"); +app.set("layout", "layouts/main"); +app.disable("x-powered-by"); + +app.use(express.json()); +app.use(express.json({ limit: '1mb' })); +app.use(express.urlencoded({ extended: true })); +// Archivos estáticos que fuerzan la re-descarga de arhivos +app.use(favicon(path.join(__dirname, 'public', 'favicon', 'favicon.ico'), { maxAge: '1y' })); +app.use(express.static(path.join(__dirname, "public"), { + etag: false, maxAge: 0, + setHeaders: (res, path) => { + res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + } +})); +app.use(expressLayouts); +app.use(cookieParser(process.env.SESSION_SECRET)); +app.use(expressPages); // Renderizado trae las paginas desde ./services/manso/src/routes/routes.js + + +// ---------------------------------------------------------- +// Uso de API v1 +// ---------------------------------------------------------- +app.use("/api/v1", v1Router); + +// /api/rpc/get_producto/:id +// /api/v1/rpc/get_producto/:id -> /rpc/get_producto/:id + + +// ---------------------------------------------------------- +// Seguridad: Tablas permitidas +// ---------------------------------------------------------- +const ALLOWED_TABLES = [ + 'roles','usuarios','usua_roles', + 'categorias','productos', + 'clientes','mesas', + 'comandas','deta_comandas', + 'proveedores','compras','deta_comp_producto', + 'mate_primas','deta_comp_materias', + 'prov_producto','prov_mate_prima', + 'receta_producto', 'asistencia_resumen_diario', + 'asistencia_intervalo', 'asistencia_detalle', + 'vw_compras' +]; + +function ensureTable(name) { + const t = String(name || '').toLowerCase(); + if (!ALLOWED_TABLES.includes(t)) throw new Error('Tabla no permitida'); + return t; +} + + + + +// ---------------------------------------------------------- +// Introspección de esquema +// ---------------------------------------------------------- +async function loadColumns(client, table) { + const sql = ` + SELECT + c.column_name, + c.data_type, + c.is_nullable = 'YES' AS is_nullable, + c.column_default, + (SELECT EXISTS ( + SELECT 1 FROM pg_attribute a + JOIN pg_class t ON t.oid = a.attrelid + JOIN pg_index i ON i.indrelid = t.oid AND a.attnum = ANY(i.indkey) + WHERE t.relname = $1 AND i.indisprimary AND a.attname = c.column_name + )) AS is_primary, + (SELECT a.attgenerated = 's' OR a.attidentity IN ('a','d') + FROM pg_attribute a + JOIN pg_class t ON t.oid = a.attrelid + WHERE t.relname = $1 AND a.attname = c.column_name + ) AS is_identity + FROM information_schema.columns c + WHERE c.table_schema='public' AND c.table_name=$1 + ORDER BY c.ordinal_position + `; + const { rows } = await client.query(sql, [table]); + return rows; +} + +async function loadForeignKeys(client, table) { + const sql = ` + SELECT + kcu.column_name, + ccu.table_name AS foreign_table, + ccu.column_name AS foreign_column + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema + JOIN information_schema.constraint_column_usage ccu + ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema + WHERE tc.table_schema='public' AND tc.table_name=$1 AND tc.constraint_type='FOREIGN KEY' + `; + const { rows } = await client.query(sql, [table]); + const map = {}; + for (const r of rows) map[r.column_name] = { foreign_table: r.foreign_table, foreign_column: r.foreign_column }; + return map; +} + +async function loadPrimaryKey(client, table) { + const sql = ` + SELECT a.attname AS column_name + FROM pg_index i + JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) + JOIN pg_class t ON t.oid = i.indrelid + WHERE t.relname = $1 AND i.indisprimary + `; + const { rows } = await client.query(sql, [table]); + return rows.map(r => r.column_name); +} + +// label column for FK options +async function pickLabelColumn(client, refTable) { + const preferred = ['nombre','raz_social','apodo','documento','correo','telefono']; + const { rows } = await client.query( + `SELECT column_name, data_type + FROM information_schema.columns + WHERE table_schema='public' AND table_name=$1 + ORDER BY ordinal_position`, [refTable] + ); + for (const cand of preferred) { + if (rows.find(r => r.column_name === cand)) return cand; + } + const textish = rows.find(r => /text|character varying|varchar/i.test(r.data_type)); + if (textish) return textish.column_name; + return rows[0]?.column_name || 'id'; +} + +// ---------------------------------------------------------- +// Middleware para datos globales +// ---------------------------------------------------------- +app.use((req, res, next) => { + res.locals.currentPath = req.path; + res.locals.pageTitle = "SuiteCoffee"; + res.locals.pageId = ""; + next(); +}); + + + + +// ---------------------------------------------------------- +// API +// ---------------------------------------------------------- +app.get('/api/tables', async (_req, res) => { + res.json(ALLOWED_TABLES); +}); + +app.get('/api/schema/:table', async (req, res) => { + try { + const table = ensureTable(req.params.table); + const client = await pool.getClient(); + try { + const columns = await loadColumns(client, table); + const fks = await loadForeignKeys(client, table); + const enriched = columns.map(c => ({ ...c, foreign: fks[c.column_name] || null })); + res.json({ table, columns: enriched }); + } finally { client.release(); } + } catch (e) { + res.status(400).json({ error: e.message }); + } +}); + +app.get('/api/options/:table/:column', async (req, res) => { + try { + const table = ensureTable(req.params.table); + const column = req.params.column; + if (!VALID_IDENT.test(column)) throw new Error('Columna inválida'); + + const client = await pool.getClient(); + try { + const fks = await loadForeignKeys(client, table); + const fk = fks[column]; + if (!fk) return res.json([]); + + const refTable = fk.foreign_table; + const refId = fk.foreign_column; + const labelCol = await pickLabelColumn(client, refTable); + + const sql = `SELECT ${q(refId)} AS id, ${q(labelCol)} AS label FROM ${q(refTable)} ORDER BY ${q(labelCol)} LIMIT 1000`; + const result = await client.query(sql); + res.json(result.rows); + } finally { client.release(); } + } catch (e) { + res.status(400).json({ error: e.message }); + } +}); + +app.get('/api/table/:table', async (req, res) => { + try { + const table = ensureTable(req.params.table); + const limit = Math.min(parseInt(req.query.limit || '100', 10), 1000); + const client = await pool.getClient(); + try { + const pks = await loadPrimaryKey(client, table); + const orderBy = pks.length ? `ORDER BY ${pks.map(q).join(', ')} DESC` : ''; + const sql = `SELECT * FROM ${q(table)} ${orderBy} LIMIT ${limit}`; + const result = await client.query(sql); + + // Normalizar: siempre devolver objetos {col: valor} + const colNames = result.fields.map(f => f.name); + let rows = result.rows; + if (rows.length && Array.isArray(rows[0])) { + rows = rows.map(r => Object.fromEntries(r.map((v, i) => [colNames[i], v]))); + } + res.json(rows); + } finally { client.release(); } + } catch (e) { + res.status(400).json({ error: e.message, code: e.code, detail: e.detail }); + } +}); + +app.post('/api/table/:table', async (req, res) => { + const table = ensureTable(req.params.table); + const payload = req.body || {}; + try { + const client = await pool.getClient(); + try { + const columns = await loadColumns(client, table); + const insertable = columns.filter(c => + !c.is_primary && !c.is_identity && !(c.column_default || '').startsWith('nextval(') + ); + const allowedCols = new Set(insertable.map(c => c.column_name)); + + const cols = []; + const vals = []; + const params = []; + let idx = 1; + for (const [k, v] of Object.entries(payload)) { + if (!allowedCols.has(k)) continue; + if (!VALID_IDENT.test(k)) continue; + cols.push(q(k)); + vals.push(`$${idx++}`); + params.push(v); + } + + if (!cols.length) { + const { rows } = await client.query(`INSERT INTO ${q(table)} DEFAULT VALUES RETURNING *`); + res.status(201).json({ inserted: rows[0] }); + } else { + const { rows } = await client.query( + `INSERT INTO ${q(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')}) RETURNING *`, + params + ); + res.status(201).json({ inserted: rows[0] }); + } + } catch (e) { + if (e.code === '23503') return res.status(400).json({ error: 'Violación de clave foránea', detail: e.detail }); + if (e.code === '23505') return res.status(400).json({ error: 'Violación de unicidad', detail: e.detail }); + if (e.code === '23514') return res.status(400).json({ error: 'Violación de CHECK', detail: e.detail }); + if (e.code === '23502') return res.status(400).json({ error: 'Campo NOT NULL faltante', detail: e.detail }); + throw e; + } finally { client.release(); } + } catch (e) { + res.status(400).json({ error: e.message }); + } +}); + + + + + + + + + +// ---------------------------------------------------------- +// Verificación de conexión +// ---------------------------------------------------------- + +async function verificarConexionCore() { + try { + console.log(`[APP] Comprobando accesibilidad a la db ${CORE_DB_NAME} del host ${CORE_DB_HOST} ...`); + const client = await poolCore.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[APP] Conexión con ${CORE_DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[APP] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[APP] Revisar credenciales, accesos de red y firewall.'); + } +} +async function verificarConexionTenants() { + try { + console.log(`[APP] Comprobando accesibilidad a la db ${TENANTS_DB_NAME} del host ${TENANTS_DB_HOST} ...`); + const client = await poolTenants.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[APP] Conexión con ${TENANTS_DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[APP] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[APP] Revisar credenciales, accesos de red y firewall.'); + } +} + +// ----------------------------------------------------------------------------- +// 404 + Manejo de errores +// ----------------------------------------------------------------------------- + +/*app.use((req, res) => res.status(404).json({ error: 'Error 404, No se encontró la página', path: req.originalUrl })); + +app.use((err, _req, res, _next) => { + console.error('[APP] ', err); + if (res.headersSent) return; + res.status(500).json({ error: '¡Oh! A ocurrido un error en el servidor app.', detail: err.stack || String(err) }); +});*/ + +// ---------------------------------------------------------- +// Inicio del servidor +// ---------------------------------------------------------- + +app.listen(PORT, () => { + console.log(`[APP] SuiteCoffee corriendo en http://localhost:${PORT}`); + verificarConexionCore(); + verificarConexionTenants(); +}); + +// ----------------------------------------------------------------------------- +// Healthcheck +// ----------------------------------------------------------------------------- +app.get('/health', (_req, res) => { + res.status(200).json({ status: 'ok'}), + console.log(`[APP] Saludable`) +}); \ No newline at end of file diff --git a/services/app/src/pages/pages.js b/services/app/src/pages/pages.js new file mode 100644 index 0000000..68d657a --- /dev/null +++ b/services/app/src/pages/pages.js @@ -0,0 +1,67 @@ +// services/manso/src/api/v1/routes/routes.js + +import { Router } from 'express'; + +const router = Router(); + +// ---------------------------------------------------------- +// Rutas de UI +// ---------------------------------------------------------- + +router.get('/', (req, res) => { + res.locals.pageTitle = "Inicio"; // Título de pestaña + res.locals.pageId = "home"; // Sidebar contextual + res.render("dashboard"); // Archivo .ejs a renderizar + // res.json({ ok: true, route: '/inicio' }); // Debug json +}); + +router.get('/dashboard', (req, res) => { + res.locals.pageTitle = "Dashboard"; + res.locals.pageId = "dashboard"; + res.render("dashboard"); + // res.json({ ok: true, route: '/dashboard' }); +}); + +router.get('/comandas', (req, res) => { + res.locals.pageTitle = "Comandas"; + res.locals.pageId = "comandas"; + res.render("comandas"); + // res.json({ ok: true, route: '/comandas' }); +}); + +router.get('/estadoComandas', (req, res) => { + res.locals.pageTitle = "Estado"; + res.locals.pageId = "estadoComandas"; + res.render("estadoComandas"); + // res.json({ ok: true, route: '/estadoComandas' }); +}); + +router.get('/productos', (req, res) => { + res.locals.pageTitle = "Propductos"; + res.locals.pageId = "productos"; + res.render("productos"); + // res.json({ ok: true, route: '/productos' }); +}); + +router.get('/usuarios', (req, res) => { + res.locals.pageTitle = "Usuarios"; + res.locals.pageId = "usuarios"; + res.render("usuarios"); + // res.json({ ok: true, route: '/usuarios' }); +}); + +router.get('/reportes', (req, res) => { + res.locals.pageTitle = "Reportes"; + res.locals.pageId = "reportes"; + res.render("reportes"); + // res.json({ ok: true, route: '/reportes' }); +}); + +router.get('/compras', (req, res) => { + res.locals.pageTitle = "Compras"; + res.locals.pageId = "compras"; + res.render("compras"); + // res.json({ ok: true, route: '/compras' }); +}); + +export default router; \ No newline at end of file diff --git a/services/auth/.env.development b/services/auth/.env.development index 8775b30..79f86e5 100644 --- a/services/auth/.env.development +++ b/services/auth/.env.development @@ -2,43 +2,61 @@ NODE_ENV=development PORT=4040 + # ===== Session (usa el Redis del stack) ===== # Para DEV podemos reutilizar el Redis de Authentik. En prod conviene uno separado. SESSION_SECRET=Neon*Mammal*Boaster*Ludicrous*Fender8*Crablike SESSION_COOKIE_NAME=sc.sid -REDIS_URL=redis://ak-redis:6379 # ===== DB principal (metadatos de SuiteCoffee) ===== # Usa el alias de red del servicio 'db' (compose: aliases [dev-db]) DB_HOST=dev-db +DB_NAME=dev_suitecoffee_core DB_PORT=5432 -DB_NAME=dev-suitecoffee DB_USER=dev-user-suitecoffee DB_PASS=dev-pass-suitecoffee +CORE_DB_HOST=dev-db +CORE_DB_NAME=dev_suitecoffee_core +CORE_DB_PORT=5432 +CORE_DB_USER=dev-user-suitecoffee +CORE_DB_PASS=dev-pass-suitecoffee + # ===== DB tenants (Tenants de SuiteCoffee) ===== TENANTS_HOST=dev-tenants -TENANTS_DB=dev-postgres -TENANTS_USER=dev-user-postgres -TENANTS_PASS=dev-pass-postgres +TENANTS_DB=dev_suitecoffee_tenants TENANTS_PORT=5432 +TENANTS_USER=suitecoffee +TENANTS_PASS=suitecoffee + +TENANTS_DB_HOST=dev-tenants +TENANTS_DB_NAME=dev_suitecoffee_tenants +TENANTS_DB_PORT=5432 +TENANTS_DB_USER=suitecoffee +TENANTS_DB_PASS=suitecoffee -TENANT_INIT_SQL=/app/src/db/initTenant_v2.sql # ===== Authentik — Admin API (server-to-server dentro de la red) ===== # Usa el alias de red del servicio 'authentik' y su puerto interno 9000 -AUTHENTIK_TOKEN=h2apVHbd3ApMcnnSwfQPXbvximkvP8HnUE25ot3zXWuEEtJFaNCcOzDHB6Xw -AUTH_CALLBACK_URL=https://suitecoffee.uy/auth/callback +AK_TOKEN=h2apVHbd3ApMcnnSwfQPXbvximkvP8HnUE25ot3zXWuEEtJFaNCcOzDHB6Xw +AK_REDIS_URL=redis://ak-redis:6379 # ===== OIDC (DEBE coincidir con el Provider) ===== # DEV (todo dentro de la red de Docker): # - El auth service redirige al navegador a este issuer. Si NO tenés reverse proxy hacia Authentik, # esta URL interna NO será accesible desde el navegador del host. En ese caso, ver nota más abajo. -# AUTHENTIK_ISSUER=https://sso.suitecoffee.mateosaldain.uy/application/o/suitecoffee/ -AUTHENTIK_ISSUER=https://sso.suitecoffee.uy/application/o/suitecoffee/ -OIDC_CLIENT_ID=1orMM8vOvf3WkN2FejXYvUFpPtONG0Lx1eMlwIpW -OIDC_CLIENT_SECRET=t5wx13qBcM0EFQ3cGnUIAmLzvbdsQrUVPv1OGWjszWkEp35pJQ55t7vZeeShqG49kuRAaiXv6PSGJLhRfGaponGaJl8gH1uCL7KIxdmm7UihgYoAXB2dFhZV4zRxfze2 -OIDC_REDIRECT_URI=https://suitecoffee.uy/auth/callback +APP_BASE_URL=https://suitecoffee.uy -OIDC_ENROLLMENT_URL=https://sso.suitecoffee.uy/if/flow/registro-suitecoffee/ \ No newline at end of file +OIDC_LOGIN_URL=https://sso.suitecoffee.uy +OIDC_REDIRECT_URI = https://suitecoffee.uy/auth/callback + +OIDC_CLIEN_ID=1orMM8vOvf3WkN2FejXYvUFpPtONG0Lx1eMlwIpW +OIDC_CONFIG_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/.well-known/openid-configuration +OIDC_ISSUER=https://sso.suitecoffee.uy/application/o/suitecoffee/ +OIDC_ISSUER_DISCOVERY=https://sso.suitecoffee.uy/application/o/suitecoffee/.well-known/openid-configuration +OIDC_AUTHORIZE_URL=https://sso.suitecoffee.uy/application/o/authorize/ +OIDC_TOKEN_URL=https://sso.suitecoffee.uy/application/o/token/ +OIDC_USERINFO_URL=https://sso.suitecoffee.uy/application/o/userinfo/ +OIDC_LOGOUT_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/end-session/ +OIDC_JWKS_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/jwks/ \ No newline at end of file diff --git a/services/auth/Dockerfile.production b/services/auth/Dockerfile.production index 0cd75d0..b4d514f 100644 --- a/services/auth/Dockerfile.production +++ b/services/auth/Dockerfile.production @@ -1,5 +1,5 @@ # Dockerfile.dev -FROM node:22.18 +FROM node:20.19.5-bookworm # Definir variables de entorno con valores predeterminados # ARG NODE_ENV=production diff --git a/services/auth/package-lock.json b/services/auth/package-lock.json index 0955b96..cb73938 100644 --- a/services/auth/package-lock.json +++ b/services/auth/package-lock.json @@ -13,6 +13,7 @@ "bcrypt": "^5.1.1", "chalk": "^5.6.0", "connect-redis": "^9.0.0", + "cookie-parser": "^1.4.7", "cookie-session": "^2.0.0", "cors": "^2.8.5", "dotenv": "^17.2.1", @@ -24,6 +25,7 @@ "jose": "^6.1.0", "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.2.0", + "node-appwrite": "^20.2.1", "node-fetch": "^3.3.2", "openid-client": "^5.7.1", "pg": "^8.16.3", @@ -37,21 +39,15 @@ }, "node_modules/@epic-web/invariant": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", - "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", "dev": true, "license": "MIT" }, "node_modules/@ioredis/commands": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.3.1.tgz", - "integrity": "sha512-bYtU8avhGIcje3IhvF9aSjsa5URMZBHnwKtOvXsT4sfYy9gppW11gLPT/9oNqlJZD47yPKveQFTAFWpHjKvUoQ==", "license": "MIT" }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "license": "BSD-3-Clause", "dependencies": { "detect-libc": "^2.0.0", @@ -70,8 +66,6 @@ }, "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -90,8 +84,6 @@ }, "node_modules/@redis/bloom": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.2.tgz", - "integrity": "sha512-855DR0ChetZLarblio5eM0yLwxA9Dqq50t8StXKp5bAtLT0G+rZ+eRzzqxl37sPqQKjUudSYypz55o6nNhbz0A==", "license": "MIT", "engines": { "node": ">= 18" @@ -102,8 +94,6 @@ }, "node_modules/@redis/client": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.2.tgz", - "integrity": "sha512-WtMScno3+eBpTac1Uav2zugXEoXqaU23YznwvFgkPwBQVwEHTDgOG7uEAObtZ/Nyn8SmAMbqkEubJaMOvnqdsQ==", "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2" @@ -114,8 +104,6 @@ }, "node_modules/@redis/json": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.2.tgz", - "integrity": "sha512-uxpVfas3I0LccBX9rIfDgJ0dBrUa3+0Gc8sEwmQQH0vHi7C1Rx1Qn8Nv1QWz5bohoeIXMICFZRcyDONvum2l/w==", "license": "MIT", "engines": { "node": ">= 18" @@ -126,8 +114,6 @@ }, "node_modules/@redis/search": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.2.tgz", - "integrity": "sha512-cNv7HlgayavCBXqPXgaS97DRPVWFznuzsAmmuemi2TMCx5scwLiP50TeZvUS06h/MG96YNPe6A0Zt57yayfxwA==", "license": "MIT", "engines": { "node": ">= 18" @@ -138,8 +124,6 @@ }, "node_modules/@redis/time-series": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.2.tgz", - "integrity": "sha512-g2NlHM07fK8H4k+613NBsk3y70R2JIM2dPMSkhIjl2Z17SYvaYKdusz85d7VYOrZBWtDrHV/WD2E3vGu+zni8A==", "license": "MIT", "engines": { "node": ">= 18" @@ -150,8 +134,6 @@ }, "node_modules/@types/body-parser": { "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "license": "MIT", "dependencies": { "@types/connect": "*", @@ -160,8 +142,6 @@ }, "node_modules/@types/connect": { "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -169,8 +149,6 @@ }, "node_modules/@types/express": { "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", "license": "MIT", "dependencies": { "@types/body-parser": "*", @@ -181,8 +159,6 @@ }, "node_modules/@types/express-serve-static-core": { "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -193,14 +169,10 @@ }, "node_modules/@types/http-errors": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "license": "MIT" }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { "@types/ms": "*", @@ -209,20 +181,14 @@ }, "node_modules/@types/mime": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "license": "MIT" }, "node_modules/@types/ms": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { "version": "24.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", - "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", "license": "MIT", "dependencies": { "undici-types": "~7.10.0" @@ -230,20 +196,14 @@ }, "node_modules/@types/qs": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -252,8 +212,6 @@ }, "node_modules/@types/serve-static": { "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -263,14 +221,10 @@ }, "node_modules/abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "license": "ISC" }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -282,8 +236,6 @@ }, "node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", "dependencies": { "debug": "4" @@ -294,8 +246,6 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" @@ -303,8 +253,6 @@ }, "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -317,15 +265,10 @@ }, "node_modules/aproba": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", - "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", "license": "ISC" }, "node_modules/are-we-there-yet": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", "license": "ISC", "dependencies": { "delegates": "^1.0.0", @@ -337,8 +280,6 @@ }, "node_modules/async": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "license": "MIT" }, "node_modules/asynckit": { @@ -348,9 +289,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -360,14 +301,10 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/bcrypt": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", - "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -380,8 +317,6 @@ }, "node_modules/binary-extensions": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", "engines": { @@ -393,8 +328,6 @@ }, "node_modules/body-parser": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -413,8 +346,6 @@ }, "node_modules/brace-expansion": { "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -423,8 +354,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { @@ -436,14 +365,10 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -464,8 +389,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -480,8 +403,6 @@ }, "node_modules/chalk": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", - "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -492,8 +413,6 @@ }, "node_modules/chokidar": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -517,8 +436,6 @@ }, "node_modules/chownr": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "license": "ISC", "engines": { "node": ">=10" @@ -526,8 +443,6 @@ }, "node_modules/cluster-key-slot": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", "license": "Apache-2.0", "engines": { "node": ">=0.10.0" @@ -535,8 +450,6 @@ }, "node_modules/color-support": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "license": "ISC", "bin": { "color-support": "bin.js" @@ -556,14 +469,10 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, "node_modules/connect-redis": { "version": "9.0.0", - "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-9.0.0.tgz", - "integrity": "sha512-QwzyvUePTMvEzG1hy45gZYw3X3YHrjmEdSkayURlcZft7hqadQ3X39wYkmCqblK2rGlw+XItELYt6GnyG6DEIQ==", "license": "MIT", "engines": { "node": ">=18" @@ -575,14 +484,10 @@ }, "node_modules/console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "license": "ISC" }, "node_modules/content-disposition": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -593,8 +498,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -609,10 +512,27 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-session": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-2.1.1.tgz", - "integrity": "sha512-ji3kym/XZaFVew1+tIZk5ZLp9Z/fLv9rK1aZmpug0FsgE7Cu3ZDrUdRo7FT9vFjMYfNimrrUHJzywDwT7XEFlg==", "license": "MIT", "dependencies": { "cookies": "0.9.1", @@ -626,8 +546,6 @@ }, "node_modules/cookie-session/node_modules/debug": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "license": "MIT", "dependencies": { "ms": "^2.1.1" @@ -635,8 +553,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -644,8 +560,6 @@ }, "node_modules/cookies": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", - "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -657,8 +571,6 @@ }, "node_modules/cors": { "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -670,8 +582,6 @@ }, "node_modules/cross-env": { "version": "10.0.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz", - "integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -688,8 +598,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -703,8 +611,6 @@ }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "license": "MIT", "engines": { "node": ">= 12" @@ -712,8 +618,6 @@ }, "node_modules/debug": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -738,14 +642,10 @@ }, "node_modules/delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "license": "MIT" }, "node_modules/denque": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", "license": "Apache-2.0", "engines": { "node": ">=0.10" @@ -753,8 +653,6 @@ }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -762,8 +660,6 @@ }, "node_modules/detect-libc": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -771,8 +667,6 @@ }, "node_modules/dotenv": { "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -797,8 +691,6 @@ }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -806,14 +698,10 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/ejs": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" @@ -827,14 +715,10 @@ }, "node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -887,14 +771,10 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -902,8 +782,6 @@ }, "node_modules/express": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -943,14 +821,10 @@ } }, "node_modules/express-ejs-layouts": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/express-ejs-layouts/-/express-ejs-layouts-2.5.1.tgz", - "integrity": "sha512-IXROv9n3xKga7FowT06n1Qn927JR8ZWDn5Dc9CJQoiiaaDqbhW5PDmWShzbpAa2wjWT1vJqaIM1S6vJwwX11gA==" + "version": "2.5.1" }, "node_modules/express-session": { "version": "1.18.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", - "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "license": "MIT", "dependencies": { "cookie": "0.7.2", @@ -968,14 +842,10 @@ }, "node_modules/express-session/node_modules/cookie-signature": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -983,14 +853,10 @@ }, "node_modules/express-session/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { "type": "github", @@ -1012,8 +878,6 @@ }, "node_modules/filelist": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" @@ -1021,8 +885,6 @@ }, "node_modules/filelist/node_modules/brace-expansion": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1030,8 +892,6 @@ }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -1042,8 +902,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -1055,8 +913,6 @@ }, "node_modules/finalhandler": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -1129,8 +985,6 @@ }, "node_modules/formdata-polyfill": { "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -1141,8 +995,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1150,8 +1002,6 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1159,8 +1009,6 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -1171,8 +1019,6 @@ }, "node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -1183,25 +1029,8 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1213,9 +1042,6 @@ }, "node_modules/gauge": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -1271,9 +1097,6 @@ }, "node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -1292,8 +1115,6 @@ }, "node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -1317,8 +1138,6 @@ }, "node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { @@ -1354,8 +1173,6 @@ }, "node_modules/has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "license": "ISC" }, "node_modules/hasown": { @@ -1372,8 +1189,6 @@ }, "node_modules/http-errors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { "depd": "2.0.0", @@ -1388,8 +1203,6 @@ }, "node_modules/http-errors/node_modules/statuses": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1397,8 +1210,6 @@ }, "node_modules/https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "dependencies": { "agent-base": "6", @@ -1410,8 +1221,6 @@ }, "node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -1422,16 +1231,11 @@ }, "node_modules/ignore-by-default": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true, "license": "ISC" }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -1440,14 +1244,10 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ioredis": { "version": "5.7.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.7.0.tgz", - "integrity": "sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==", "license": "MIT", "dependencies": { "@ioredis/commands": "^1.3.0", @@ -1470,8 +1270,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -1479,8 +1277,6 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { @@ -1492,8 +1288,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -1502,8 +1296,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" @@ -1511,8 +1303,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -1524,8 +1314,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { @@ -1534,21 +1322,15 @@ }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/jake": { "version": "10.9.4", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", - "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", "license": "Apache-2.0", "dependencies": { "async": "^3.2.6", @@ -1564,8 +1346,6 @@ }, "node_modules/jose": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz", - "integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -1573,8 +1353,6 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "license": "MIT", "dependencies": { "jws": "^3.2.2", @@ -1595,8 +1373,6 @@ }, "node_modules/jwa": { "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -1606,8 +1382,6 @@ }, "node_modules/jwks-rsa": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", - "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", "license": "MIT", "dependencies": { "@types/express": "^4.17.20", @@ -1623,8 +1397,6 @@ }, "node_modules/jwks-rsa/node_modules/jose": { "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -1632,8 +1404,6 @@ }, "node_modules/jws": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "license": "MIT", "dependencies": { "jwa": "^1.4.1", @@ -1642,8 +1412,6 @@ }, "node_modules/keygrip": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", "license": "MIT", "dependencies": { "tsscmp": "1.0.6" @@ -1653,74 +1421,50 @@ } }, "node_modules/limiter": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", - "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + "version": "1.1.5" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "license": "MIT" }, "node_modules/lodash.defaults": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, "node_modules/lodash.isarguments": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -1731,8 +1475,6 @@ }, "node_modules/lru-memoizer": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", - "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", "license": "MIT", "dependencies": { "lodash.clonedeep": "^4.5.0", @@ -1741,8 +1483,6 @@ }, "node_modules/make-dir": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "license": "MIT", "dependencies": { "semver": "^6.0.0" @@ -1756,8 +1496,6 @@ }, "node_modules/make-dir/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1774,8 +1512,6 @@ }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1783,8 +1519,6 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -1795,8 +1529,6 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1804,8 +1536,6 @@ }, "node_modules/mime-types": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" @@ -1816,8 +1546,6 @@ }, "node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1828,8 +1556,6 @@ }, "node_modules/minipass": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "license": "ISC", "engines": { "node": ">=8" @@ -1837,8 +1563,6 @@ }, "node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -1850,8 +1574,6 @@ }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -1862,8 +1584,6 @@ }, "node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -1874,14 +1594,10 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1889,15 +1605,19 @@ }, "node_modules/node-addon-api": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", "license": "MIT" }, + "node_modules/node-appwrite": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-20.2.1.tgz", + "integrity": "sha512-RweIh+3RHjprsxhWaJzcQr/UDMBMsZCma50TIJ9t3onVgs5jAT9aqFnsMlaaC9QZn1sXpPUQV90W6uvtm64DnQ==", + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-native-with-agent": "1.7.2" + } + }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -1915,8 +1635,6 @@ }, "node_modules/node-fetch": { "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -1931,10 +1649,14 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-fetch-native-with-agent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-fetch-native-with-agent/-/node-fetch-native-with-agent-1.7.2.tgz", + "integrity": "sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g==", + "license": "MIT" + }, "node_modules/nodemon": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", "dev": true, "license": "MIT", "dependencies": { @@ -1962,8 +1684,6 @@ }, "node_modules/nopt": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "license": "ISC", "dependencies": { "abbrev": "1" @@ -1977,8 +1697,6 @@ }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { @@ -1987,9 +1705,6 @@ }, "node_modules/npmlog": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", "license": "ISC", "dependencies": { "are-we-there-yet": "^2.0.0", @@ -2000,8 +1715,6 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2009,8 +1722,6 @@ }, "node_modules/object-hash": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "license": "MIT", "engines": { "node": ">= 6" @@ -2018,8 +1729,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2030,8 +1739,6 @@ }, "node_modules/oidc-token-hash": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.1.tgz", - "integrity": "sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==", "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" @@ -2039,8 +1746,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -2051,8 +1756,6 @@ }, "node_modules/on-headers": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2060,8 +1763,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -2069,8 +1770,6 @@ }, "node_modules/openid-client": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", - "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", "license": "MIT", "dependencies": { "jose": "^4.15.9", @@ -2084,8 +1783,6 @@ }, "node_modules/openid-client/node_modules/jose": { "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -2093,8 +1790,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2102,8 +1797,6 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2111,8 +1804,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -2121,8 +1812,6 @@ }, "node_modules/path-to-regexp": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "license": "MIT", "engines": { "node": ">=16" @@ -2170,8 +1859,6 @@ }, "node_modules/pg-format": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz", - "integrity": "sha512-YyKEF78pEA6wwTAqOUaHIN/rWpfzzIuMh9KdAhc3rSLQ/7zkRFcCgYBAEGatDstLyZw4g0s9SNICmaTGnBVeyw==", "license": "MIT", "engines": { "node": ">=4.0" @@ -2228,14 +1915,10 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -2286,8 +1969,6 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -2305,15 +1986,11 @@ }, "node_modules/pstree.remy": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true, "license": "MIT" }, "node_modules/qs": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -2327,8 +2004,6 @@ }, "node_modules/random-bytes": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2336,8 +2011,6 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2345,8 +2018,6 @@ }, "node_modules/raw-body": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -2360,8 +2031,6 @@ }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -2374,8 +2043,6 @@ }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { @@ -2387,8 +2054,6 @@ }, "node_modules/redis": { "version": "5.8.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-5.8.2.tgz", - "integrity": "sha512-31vunZj07++Y1vcFGcnNWEf5jPoTkGARgfWI4+Tk55vdwHxhAvug8VEtW7Cx+/h47NuJTEg/JL77zAwC6E0OeA==", "license": "MIT", "dependencies": { "@redis/bloom": "5.8.2", @@ -2403,8 +2068,6 @@ }, "node_modules/redis-errors": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", "license": "MIT", "engines": { "node": ">=4" @@ -2412,8 +2075,6 @@ }, "node_modules/redis-parser": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "license": "MIT", "dependencies": { "redis-errors": "^1.0.0" @@ -2424,9 +2085,6 @@ }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -2440,8 +2098,6 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -2456,8 +2112,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -2476,14 +2130,10 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/semver": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -2494,8 +2144,6 @@ }, "node_modules/send": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", "dependencies": { "debug": "^4.3.5", @@ -2516,8 +2164,6 @@ }, "node_modules/serve-static": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -2531,20 +2177,14 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "license": "ISC" }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { @@ -2556,8 +2196,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { @@ -2566,8 +2204,6 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2585,8 +2221,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2601,8 +2235,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -2619,8 +2251,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -2638,14 +2268,10 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, "node_modules/simple-update-notifier": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, "license": "MIT", "dependencies": { @@ -2666,14 +2292,10 @@ }, "node_modules/standard-as-callback": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", "license": "MIT" }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2681,8 +2303,6 @@ }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -2690,8 +2310,6 @@ }, "node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -2704,8 +2322,6 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -2716,8 +2332,6 @@ }, "node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { @@ -2729,8 +2343,6 @@ }, "node_modules/tar": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -2746,8 +2358,6 @@ }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2759,8 +2369,6 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -2768,8 +2376,6 @@ }, "node_modules/touch": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "dev": true, "license": "ISC", "bin": { @@ -2778,14 +2384,10 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/tsscmp": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", "license": "MIT", "engines": { "node": ">=0.6.x" @@ -2793,8 +2395,6 @@ }, "node_modules/type-is": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -2807,8 +2407,6 @@ }, "node_modules/uid-safe": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", "license": "MIT", "dependencies": { "random-bytes": "~1.0.0" @@ -2819,21 +2417,15 @@ }, "node_modules/undefsafe": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true, "license": "MIT" }, "node_modules/undici-types": { "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2841,14 +2433,10 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2856,8 +2444,6 @@ }, "node_modules/web-streams-polyfill": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "license": "MIT", "engines": { "node": ">= 8" @@ -2865,14 +2451,10 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -2881,8 +2463,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { @@ -2897,8 +2477,6 @@ }, "node_modules/wide-align": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -2906,8 +2484,6 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/xtend": { @@ -2921,8 +2497,6 @@ }, "node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" } } diff --git a/services/auth/package.json b/services/auth/package.json index 41dcffa..add584b 100644 --- a/services/auth/package.json +++ b/services/auth/package.json @@ -1,11 +1,11 @@ { "name": "authentication", "version": "1.0.0", - "main": "src/index.js", + "main": "src/index.mjs", "scripts": { - "start": "NODE_ENV=production node ./src/index.js", - "dev": "NODE_ENV=development npx nodemon ./src/index.js", - "test": "NODE_ENV=stage node ./src/index.js" + "start": "NODE_ENV=production node ./src/index.mjs", + "dev": "NODE_ENV=development npx nodemon ./src/index.mjs", + "test": "NODE_ENV=stage node ./src/index.mjs" }, "author": "Mateo Saldain", "license": "ISC", @@ -19,6 +19,7 @@ "bcrypt": "^5.1.1", "chalk": "^5.6.0", "connect-redis": "^9.0.0", + "cookie-parser": "^1.4.7", "cookie-session": "^2.0.0", "cors": "^2.8.5", "dotenv": "^17.2.1", @@ -30,12 +31,18 @@ "jose": "^6.1.0", "jsonwebtoken": "^9.0.2", "jwks-rsa": "^3.2.0", + "node-appwrite": "^20.2.1", "node-fetch": "^3.3.2", "openid-client": "^5.7.1", "pg": "^8.16.3", "pg-format": "^1.0.4", "redis": "^5.8.2" }, + "imports": { + "#v1Router": "./src/api/v1/routes/routes.js", + "#pages": "./src/pages/pages.js", + "#db": "./src/db/poolSingleton.js" + }, "keywords": [], "description": "" } diff --git a/services/auth/src/api/api.js b/services/auth/src/api/api.js new file mode 100644 index 0000000..d65d8b8 --- /dev/null +++ b/services/auth/src/api/api.js @@ -0,0 +1,181 @@ +// // ---------------------------------------------------------- +// // API +// // ---------------------------------------------------------- +// app.get('/api/tables', async (_req, res) => { +// res.json(ALLOWED_TABLES); +// }); + +// app.get('/api/schema/:table', async (req, res) => { +// try { +// const table = ensureTable(req.params.table); +// const client = await getClient(); +// try { +// const columns = await loadColumns(client, table); +// const fks = await loadForeignKeys(client, table); +// const enriched = columns.map(c => ({ ...c, foreign: fks[c.column_name] || null })); +// res.json({ table, columns: enriched }); +// } finally { client.release(); } +// } catch (e) { +// res.status(400).json({ error: e.message }); +// } +// }); + +// app.get('/api/options/:table/:column', async (req, res) => { +// try { +// const table = ensureTable(req.params.table); +// const column = req.params.column; +// if (!VALID_IDENT.test(column)) throw new Error('Columna inválida'); + +// const client = await getClient(); +// try { +// const fks = await loadForeignKeys(client, table); +// const fk = fks[column]; +// if (!fk) return res.json([]); + +// const refTable = fk.foreign_table; +// const refId = fk.foreign_column; +// const labelCol = await pickLabelColumn(client, refTable); + +// const sql = `SELECT ${q(refId)} AS id, ${q(labelCol)} AS label FROM ${q(refTable)} ORDER BY ${q(labelCol)} LIMIT 1000`; +// const result = await client.query(sql); +// res.json(result.rows); +// } finally { client.release(); } +// } catch (e) { +// res.status(400).json({ error: e.message }); +// } +// }); + +// app.get('/api/table/:table', async (req, res) => { +// try { +// const table = ensureTable(req.params.table); +// const limit = Math.min(parseInt(req.query.limit || '100', 10), 1000); +// const client = await getClient(); +// try { +// const pks = await loadPrimaryKey(client, table); +// const orderBy = pks.length ? `ORDER BY ${pks.map(q).join(', ')} DESC` : ''; +// const sql = `SELECT * FROM ${q(table)} ${orderBy} LIMIT ${limit}`; +// const result = await client.query(sql); + +// // Normalizar: siempre devolver objetos {col: valor} +// const colNames = result.fields.map(f => f.name); +// let rows = result.rows; +// if (rows.length && Array.isArray(rows[0])) { +// rows = rows.map(r => Object.fromEntries(r.map((v, i) => [colNames[i], v]))); +// } +// res.json(rows); +// } finally { client.release(); } +// } catch (e) { +// res.status(400).json({ error: e.message, code: e.code, detail: e.detail }); +// } +// }); + +// app.post('/api/table/:table', async (req, res) => { +// const table = ensureTable(req.params.table); +// const payload = req.body || {}; +// try { +// const client = await getClient(); +// try { +// const columns = await loadColumns(client, table); +// const insertable = columns.filter(c => +// !c.is_primary && !c.is_identity && !(c.column_default || '').startsWith('nextval(') +// ); +// const allowedCols = new Set(insertable.map(c => c.column_name)); + +// const cols = []; +// const vals = []; +// const params = []; +// let idx = 1; +// for (const [k, v] of Object.entries(payload)) { +// if (!allowedCols.has(k)) continue; +// if (!VALID_IDENT.test(k)) continue; +// cols.push(q(k)); +// vals.push(`$${idx++}`); +// params.push(v); +// } + +// if (!cols.length) { +// const { rows } = await client.query(`INSERT INTO ${q(table)} DEFAULT VALUES RETURNING *`); +// res.status(201).json({ inserted: rows[0] }); +// } else { +// const { rows } = await client.query( +// `INSERT INTO ${q(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')}) RETURNING *`, +// params +// ); +// res.status(201).json({ inserted: rows[0] }); +// } +// } catch (e) { +// if (e.code === '23503') return res.status(400).json({ error: 'Violación de clave foránea', detail: e.detail }); +// if (e.code === '23505') return res.status(400).json({ error: 'Violación de unicidad', detail: e.detail }); +// if (e.code === '23514') return res.status(400).json({ error: 'Violación de CHECK', detail: e.detail }); +// if (e.code === '23502') return res.status(400).json({ error: 'Campo NOT NULL faltante', detail: e.detail }); +// throw e; +// } +// } catch (e) { +// res.status(400).json({ error: e.message }); +// } +// }); + +// app.get('/api/comandas', async (req, res, next) => { +// try { +// const estado = (req.query.estado || '').trim() || null; +// const limit = Math.min(parseInt(req.query.limit || '200', 10), 1000); + +// const { rows } = await mainPool.query( +// `SELECT * FROM public.f_comandas_resumen($1, $2)`, +// [estado, limit] +// ); +// res.json(rows); +// } catch (e) { next(e); } +// }); + + +// // Detalle de una comanda (con nombres de productos) +// // GET /api/comandas/:id/detalle +// app.get('/api/comandas/:id/detalle', (req, res, next) => +// mainPool.query( +// `SELECT id_det_comanda, id_producto, producto_nombre, +// cantidad, pre_unitario, subtotal, observaciones +// FROM public.v_comandas_detalle_items +// WHERE id_comanda = $1::int +// ORDER BY id_det_comanda`, +// [req.params.id] +// ) +// .then(r => res.json(r.rows)) +// .catch(next) +// ); + +// // Cerrar comanda (setea estado y fec_cierre en DB) +// app.post('/api/comandas/:id/cerrar', async (req, res, next) => { +// try { +// const id = Number(req.params.id); +// if (!Number.isInteger(id) || id <= 0) { +// return res.status(400).json({ error: 'id inválido' }); +// } +// const { rows } = await mainPool.query( +// `SELECT public.f_cerrar_comanda($1) AS data`, +// [id] +// ); +// if (!rows.length || rows[0].data === null) { +// return res.status(404).json({ error: 'Comanda no encontrada' }); +// } +// res.json(rows[0].data); +// } catch (err) { next(err); } +// }); + +// Abrir (reabrir) comanda +app.post('/api/comandas/:id/abrir', async (req, res, next) => { + try { + const id = Number(req.params.id); + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: 'id inválido' }); + } + const { rows } = await mainPool.query( + `SELECT public.f_abrir_comanda($1) AS data`, + [id] + ); + if (!rows.length || rows[0].data === null) { + return res.status(404).json({ error: 'Comanda no encontrada' }); + } + res.json(rows[0].data); + } catch (err) { next(err); } +}); \ No newline at end of file diff --git a/services/auth/src/api/rpc.js b/services/auth/src/api/rpc.js new file mode 100644 index 0000000..56766d1 --- /dev/null +++ b/services/auth/src/api/rpc.js @@ -0,0 +1,230 @@ +// // GET producto + receta +// app.get('/api/rpc/get_producto/:id', async (req, res) => { +// const id = Number(req.params.id); +// const { rows } = await mainPool.query('SELECT public.get_producto($1) AS data', [id]); +// res.json(rows[0]?.data || {}); +// }); + +// // POST guardar producto + receta + +// app.post('/api/rpc/save_producto', async (req, res) => { +// try { +// // console.debug('receta payload:', req.body?.receta); // habilitalo si lo necesitás +// const q = 'SELECT public.save_producto($1,$2,$3,$4,$5,$6,$7::jsonb) AS id_producto'; +// const { id_producto = null, nombre, img_producto = null, precio = 0, activo = true, id_categoria = null, receta = [] } = req.body || {}; +// const params = [id_producto, nombre, img_producto, precio, activo, id_categoria, JSON.stringify(receta || [])]; +// const { rows } = await mainPool.query(q, params); +// res.json(rows[0] || {}); +// } catch (e) { +// console.error(e); +// res.status(500).json({ error: 'save_producto failed' }); +// } +// }); + +// // GET MP + proveedores +// app.get('/api/rpc/get_materia/:id', async (req, res) => { +// const id = Number(req.params.id); +// try { +// const { rows } = await mainPool.query('SELECT public.get_materia_prima($1) AS data', [id]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); +// res.status(500).json({ error: 'get_materia failed' }); +// } +// }); + +// // SAVE MP + proveedores (array) +// app.post('/api/rpc/save_materia', async (req, res) => { +// const { id_mat_prima = null, nombre, unidad, activo = true, proveedores = [] } = req.body || {}; +// try { +// const q = 'SELECT public.save_materia_prima($1,$2,$3,$4,$5::jsonb) AS id_mat_prima'; +// const params = [id_mat_prima, nombre, unidad, activo, JSON.stringify(proveedores || [])]; +// const { rows } = await mainPool.query(q, params); +// res.json(rows[0] || {}); +// } catch (e) { +// console.error(e); +// res.status(500).json({ error: 'save_materia failed' }); +// } +// }); + +// // POST /api/rpc/find_usuarios_por_documentos { docs: ["12345678","09123456", ...] } +// app.post('/api/rpc/find_usuarios_por_documentos', async (req, res) => { +// try { +// const docs = Array.isArray(req.body?.docs) ? req.body.docs : []; +// const sql = 'SELECT public.find_usuarios_por_documentos($1::jsonb) AS data'; +// const { rows } = await mainPool.query(sql, [JSON.stringify(docs)]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); +// res.status(500).json({ error: 'find_usuarios_por_documentos failed' }); +// } +// }); + +// // POST /api/rpc/import_asistencia { registros: [...], origen?: "AGL_001.txt" } +// app.post('/api/rpc/import_asistencia', async (req, res) => { +// try { +// const registros = Array.isArray(req.body?.registros) ? req.body.registros : []; +// const origen = req.body?.origen || null; +// const sql = 'SELECT public.import_asistencia($1::jsonb,$2) AS data'; +// const { rows } = await mainPool.query(sql, [JSON.stringify(registros), origen]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); +// res.status(500).json({ error: 'import_asistencia failed' }); +// } +// }); + +// // Consultar datos de asistencia (raw + pares) para un usuario y rango +// app.post('/api/rpc/asistencia_get', async (req, res) => { +// try { +// const { doc, desde, hasta } = req.body || {}; +// const sql = 'SELECT public.asistencia_get($1::text,$2::date,$3::date) AS data'; +// const { rows } = await mainPool.query(sql, [doc, desde, hasta]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); res.status(500).json({ error: 'asistencia_get failed' }); +// } +// }); + +// // Editar un registro crudo y recalcular pares +// app.post('/api/rpc/asistencia_update_raw', async (req, res) => { +// try { +// const { id_raw, fecha, hora, modo } = req.body || {}; +// const sql = 'SELECT public.asistencia_update_raw($1::bigint,$2::date,$3::text,$4::text) AS data'; +// const { rows } = await mainPool.query(sql, [id_raw, fecha, hora, modo ?? null]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); res.status(500).json({ error: 'asistencia_update_raw failed' }); +// } +// }); + +// // Eliminar un registro crudo y recalcular pares +// app.post('/api/rpc/asistencia_delete_raw', async (req, res) => { +// try { +// const { id_raw } = req.body || {}; +// const sql = 'SELECT public.asistencia_delete_raw($1::bigint) AS data'; +// const { rows } = await mainPool.query(sql, [id_raw]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); res.status(500).json({ error: 'asistencia_delete_raw failed' }); +// } +// }); + +// // POST /api/rpc/report_tickets { year } +// app.post('/api/rpc/report_tickets', async (req, res) => { +// try { +// const y = parseInt(req.body?.year ?? req.query?.year, 10); +// const year = (Number.isFinite(y) && y >= 2000 && y <= 2100) +// ? y +// : (new Date()).getFullYear(); + +// const { rows } = await mainPool.query( +// 'SELECT public.report_tickets_year($1::int) AS j', [year] +// ); +// res.json(rows[0].j); +// } catch (e) { +// console.error('report_tickets error:', e); +// res.status(500).json({ +// error: 'report_tickets failed', +// message: e.message, detail: e.detail, where: e.where, code: e.code +// }); +// } +// }); + +// // POST /api/rpc/report_asistencia { desde: 'YYYY-MM-DD', hasta: 'YYYY-MM-DD' } +// app.post('/api/rpc/report_asistencia', async (req, res) => { +// try { +// let { desde, hasta } = req.body || {}; +// // defaults si vienen vacíos/invalidos +// const re = /^\d{4}-\d{2}-\d{2}$/; +// if (!re.test(desde) || !re.test(hasta)) { +// const end = new Date(); +// const start = new Date(end); start.setDate(end.getDate() - 30); +// desde = start.toISOString().slice(0, 10); +// hasta = end.toISOString().slice(0, 10); +// } + +// const { rows } = await mainPool.query( +// 'SELECT public.report_asistencia($1::date,$2::date) AS j', [desde, hasta] +// ); +// res.json(rows[0].j); +// } catch (e) { +// console.error('report_asistencia error:', e); +// res.status(500).json({ +// error: 'report_asistencia failed', +// message: e.message, detail: e.detail, where: e.where, code: e.code +// }); +// } +// }); + +// // Guardar (insert/update) +// app.post('/api/rpc/save_compra', async (req, res) => { +// try { +// const { id_compra, id_proveedor, fec_compra, detalles } = req.body || {}; +// const sql = 'SELECT * FROM public.save_compra($1::int,$2::int,$3::timestamptz,$4::jsonb)'; +// const args = [id_compra ?? null, id_proveedor, fec_compra ? new Date(fec_compra) : null, JSON.stringify(detalles)]; +// const { rows } = await mainPool.query(sql, args); +// res.json(rows[0]); // { id_compra, total } +// } catch (e) { +// console.error('save_compra error:', e); +// res.status(500).json({ error: 'save_compra failed', message: e.message, detail: e.detail, where: e.where, code: e.code }); +// } +// }); + +// // Obtener para editar +// app.post('/api/rpc/get_compra', async (req, res) => { +// try { +// const { id_compra } = req.body || {}; +// const sql = `SELECT public.get_compra($1::int) AS data`; +// const { rows } = await mainPool.query(sql, [id_compra]); +// res.json(rows[0]?.data || {}); +// } catch (e) { +// console.error(e); res.status(500).json({ error: 'get_compra failed' }); +// } +// }); + +// // Eliminar +// app.post('/api/rpc/delete_compra', async (req, res) => { +// try { +// const { id_compra } = req.body || {}; +// await mainPool.query(`SELECT public.delete_compra($1::int)`, [id_compra]); +// res.json({ ok: true }); +// } catch (e) { +// console.error(e); res.status(500).json({ error: 'delete_compra failed' }); +// } +// }); + +// // POST /api/rpc/report_gastos { year: 2025 } +// app.post('/api/rpc/report_gastos', async (req, res) => { +// try { +// const year = parseInt(req.body?.year ?? new Date().getFullYear(), 10); +// const { rows } = await mainPool.query( +// 'SELECT public.report_gastos($1::int) AS j', [year] +// ); +// res.json(rows[0].j); +// } catch (e) { +// console.error('report_gastos error:', e); +// res.status(500).json({ +// error: 'report_gastos failed', +// message: e.message, detail: e.detail, code: e.code +// }); +// } +// }); + +// // (Opcional) GET para probar rápido desde el navegador: +// // /api/rpc/report_gastos?year=2025 +// app.get('/api/rpc/report_gastos', async (req, res) => { +// try { +// const year = parseInt(req.query.year ?? new Date().getFullYear(), 10); +// const { rows } = await mainPool.query( +// 'SELECT public.report_gastos($1::int) AS j', [year] +// ); +// res.json(rows[0].j); +// } catch (e) { +// console.error('report_gastos error:', e); +// res.status(500).json({ +// error: 'report_gastos failed', +// message: e.message, detail: e.detail, code: e.code +// }); +// } +// }); \ No newline at end of file diff --git a/services/auth/src/api/v1/routes/routes.js b/services/auth/src/api/v1/routes/routes.js new file mode 100644 index 0000000..c17f2cc --- /dev/null +++ b/services/auth/src/api/v1/routes/routes.js @@ -0,0 +1,340 @@ +// services/manso/src/api/v1/routes/routes.js + +import { Router } from 'express'; +import pool from '#db'; // Pool Singleton +const router = Router(); + +// ========================================================== +// Rutas de API v1 +// ========================================================== + + + +// ---------------------------------------------------------- +// API Comandas +// ---------------------------------------------------------- + +router.route('/comandas').get( async (req, res, next) => { + try { + var client = await pool.getClient() + const estado = (req.query.estado || '').trim() || null; + const limit = Math.min(parseInt(req.query.limit || '200', 10), 1000); + + const { rows } = await client.query( + `SELECT * FROM public.f_comandas_resumen($1, $2)`, + [estado, limit] + ); + res.json(rows); + } catch (e) { + next(e); + } finally { + client.release(); + } +}); + +router.route('/comandas/:id/detalle').get( async (req, res, next) => { + try { + const client = await pool.getClient() + client.query( + `SELECT id_det_comanda, id_producto, producto_nombre, + cantidad, pre_unitario, subtotal, observaciones + FROM public.v_comandas_detalle_items + WHERE id_comanda = $1::int + ORDER BY id_det_comanda`, + [req.params.id] + ) + .then(r => res.json(r.rows)) + .catch(next) + client.release(); + } catch (error) { + next(e); + } +}); + +router.route('/comandas/:id/cerrar').post( async (req, res, next) => { + try { + const client = await pool.getClient() + const id = Number(req.params.id); + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: 'id inválido' }); + } + const { rows } = await client.query( + `SELECT public.f_cerrar_comanda($1) AS data`, + [id] + ); + if (!rows.length || rows[0].data === null) { + return res.status(404).json({ error: 'Comanda no encontrada' }); + } + res.json(rows[0].data); + client.release(); + } catch (err) { next(err); } +}); + +router.route('/comandas/:id/abrir').post( async (req, res, next) => { + try { + const client = await pool.getClient() + const id = Number(req.params.id); + if (!Number.isInteger(id) || id <= 0) { + return res.status(400).json({ error: 'id inválido' }); + } + const { rows } = await client.query( + `SELECT public.f_abrir_comanda($1) AS data`, + [id] + ); + if (!rows.length || rows[0].data === null) { + return res.status(404).json({ error: 'Comanda no encontrada' }); + } + res.json(rows[0].data); + client.release(); + } catch (err) { next(err); } +}); + + + +// ---------------------------------------------------------- +// API Productos +// ---------------------------------------------------------- + +// GET producto + receta +router.route('/rpc/get_producto/:id').get( async (req, res) => { + const client = await pool.getClient() + const id = Number(req.params.id); + const { rows } = await client.query('SELECT public.get_producto($1) AS data', [id]); + res.json(rows[0]?.data || {}); + client.release(); +}); + +// POST guardar producto + receta +router.route('/rpc/save_producto').post(async (req, res) => { + try { + // console.debug('receta payload:', req.body?.receta); // habilitalo si lo necesitás + const client = await pool.getClient() + const q = 'SELECT public.save_producto($1,$2,$3,$4,$5,$6,$7::jsonb) AS id_producto'; + const { id_producto=null, nombre, img_producto=null, precio=0, activo=true, id_categoria=null, receta=[] } = req.body || {}; + const params = [id_producto, nombre, img_producto, precio, activo, id_categoria, JSON.stringify(receta||[])]; + const { rows } = await client.query(q, params); + res.json(rows[0] || {}); + client.release(); + } catch(e) { + console.error(e); + res.status(500).json({ error: 'save_producto failed' }); + } +}); + + + +// ---------------------------------------------------------- +// API Materias Primas +// ---------------------------------------------------------- + +// GET MP + proveedores +router.route('/rpc/get_materia/:id').get(async (req, res) => { + const id = Number(req.params.id); + try { + const client = await pool.getClient() + const { rows } = await client.query('SELECT public.get_materia_prima($1) AS data', [id]); + res.json(rows[0]?.data || {}); + client.release(); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'get_materia failed' }); + } +}); + +// SAVE MP + proveedores (array) +router.route('/rpc/save_materia').post( async (req, res) => { + const { id_mat_prima=null, nombre, unidad, activo=true, proveedores=[] } = req.body || {}; + try { + const q = 'SELECT public.save_materia_prima($1,$2,$3,$4,$5::jsonb) AS id_mat_prima'; + const params = [id_mat_prima, nombre, unidad, activo, JSON.stringify(proveedores||[])]; + const { rows } = await pool.query(q, params); + res.json(rows[0] || {}); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'save_materia failed' }); + } +}); + + + +// ---------------------------------------------------------- +// API Usuarios y Asistencias +// ---------------------------------------------------------- + +// POST /api/rpc/find_usuarios_por_documentos { docs: ["12345678","09123456", ...] } +router.route('/rpc/find_usuarios_por_documentos').post( async (req, res) => { + try { + const docs = Array.isArray(req.body?.docs) ? req.body.docs : []; + const sql = 'SELECT public.find_usuarios_por_documentos($1::jsonb) AS data'; + const { rows } = await pool.query(sql, [JSON.stringify(docs)]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'find_usuarios_por_documentos failed' }); + } +}); + +// POST /api/rpc/import_asistencia { registros: [...], origen?: "AGL_001.txt" } +router.route('/rpc/import_asistencia').post( async (req, res) => { + try { + const registros = Array.isArray(req.body?.registros) ? req.body.registros : []; + const origen = req.body?.origen || null; + const sql = 'SELECT public.import_asistencia($1::jsonb,$2) AS data'; + const { rows } = await pool.query(sql, [JSON.stringify(registros), origen]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); + res.status(500).json({ error: 'import_asistencia failed' }); + } +}); + +// Consultar datos de asistencia (raw + pares) para un usuario y rango +router.route('/rpc/asistencia_get').post( async (req, res) => { + try { + const { doc, desde, hasta } = req.body || {}; + const sql = 'SELECT public.asistencia_get($1::text,$2::date,$3::date) AS data'; + const { rows } = await pool.query(sql, [doc, desde, hasta]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'asistencia_get failed' }); + } +}); + +// Editar un registro crudo y recalcular pares +router.route('/rpc/asistencia_update_raw').post( async (req, res) => { + try { + const { id_raw, fecha, hora, modo } = req.body || {}; + const sql = 'SELECT public.asistencia_update_raw($1::bigint,$2::date,$3::text,$4::text) AS data'; + const { rows } = await pool.query(sql, [id_raw, fecha, hora, modo ?? null]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'asistencia_update_raw failed' }); + } +}); + +// Eliminar un registro crudo y recalcular pares +router.route('/rpc/asistencia_delete_raw').post( async (req, res) => { + try { + const { id_raw } = req.body || {}; + const sql = 'SELECT public.asistencia_delete_raw($1::bigint) AS data'; + const { rows } = await pool.query(sql, [id_raw]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'asistencia_delete_raw failed' }); + } +}); + + +// ---------------------------------------------------------- +// API Reportes +// ---------------------------------------------------------- + +// POST /api/rpc/report_tickets { year } +router.route('/rpc/report_tickets').post( async (req, res) => { + try { + const y = parseInt(req.body?.year ?? req.query?.year, 10); + const year = (Number.isFinite(y) && y >= 2000 && y <= 2100) + ? y + : (new Date()).getFullYear(); + + const { rows } = await pool.query( + 'SELECT public.report_tickets_year($1::int) AS j', [year] + ); + res.json(rows[0].j); + } catch (e) { + console.error('report_tickets error:', e); + res.status(500).json({ + error: 'report_tickets failed', + message: e.message, detail: e.detail, where: e.where, code: e.code + }); + } +}); + +// POST /api/rpc/report_asistencia { desde: 'YYYY-MM-DD', hasta: 'YYYY-MM-DD' } +router.route('/rpc/report_asistencia').post( async (req, res) => { + try { + let { desde, hasta } = req.body || {}; + // defaults si vienen vacíos/invalidos + const re = /^\d{4}-\d{2}-\d{2}$/; + if (!re.test(desde) || !re.test(hasta)) { + const end = new Date(); + const start = new Date(end); start.setDate(end.getDate()-30); + desde = start.toISOString().slice(0,10); + hasta = end.toISOString().slice(0,10); + } + + const { rows } = await pool.query( + 'SELECT public.report_asistencia($1::date,$2::date) AS j', [desde, hasta] + ); + res.json(rows[0].j); + } catch (e) { + console.error('report_asistencia error:', e); + res.status(500).json({ + error: 'report_asistencia failed', + message: e.message, detail: e.detail, where: e.where, code: e.code + }); + } +}); + +// ---------------------------------------------------------- +// API Compras y Gastos +// ---------------------------------------------------------- + +// Guardar (insert/update) +router.route('/rpc/save_compra').post( async (req, res) => { + try { + const { id_compra, id_proveedor, fec_compra, detalles } = req.body || {}; + const sql = 'SELECT * FROM public.save_compra($1::int,$2::int,$3::timestamptz,$4::jsonb)'; + const args = [id_compra ?? null, id_proveedor, fec_compra ? new Date(fec_compra) : null, JSON.stringify(detalles)]; + const { rows } = await pool.query(sql, args); + res.json(rows[0]); // { id_compra, total } + } catch (e) { + console.error('save_compra error:', e); + res.status(500).json({ error: 'save_compra failed', message: e.message, detail: e.detail, where: e.where, code: e.code }); + } +}); + + +// Obtener para editar +router.route('/rpc/get_compra').post( async (req, res) => { + try { + const { id_compra } = req.body || {}; + const sql = `SELECT public.get_compra($1::int) AS data`; + const { rows } = await pool.query(sql, [id_compra]); + res.json(rows[0]?.data || {}); + } catch (e) { + console.error(e); res.status(500).json({ error: 'get_compra failed' }); + } +}); + +// Eliminar +router.route('/rpc/delete_compra').post( async (req, res) => { + try { + const { id_compra } = req.body || {}; + await pool.query(`SELECT public.delete_compra($1::int)`, [id_compra]); + res.json({ ok: true }); + } catch (e) { + console.error(e); res.status(500).json({ error: 'delete_compra failed' }); + } +}); + + +// POST /api/rpc/report_gastos { year: 2025 } +router.route('/rpc/report_gastos').post( async (req, res) => { + try { + const year = parseInt(req.body?.year ?? new Date().getFullYear(), 10); + const { rows } = await pool.query( + 'SELECT public.report_gastos($1::int) AS j', [year] + ); + res.json(rows[0].j); + } catch (e) { + console.error('report_gastos error:', e); + res.status(500).json({ + error: 'report_gastos failed', + message: e.message, detail: e.detail, code: e.code + }); + } +}); + + +export default router; \ No newline at end of file diff --git a/services/auth/src/db/poolSingleton.js b/services/auth/src/db/poolSingleton.js new file mode 100644 index 0000000..a0935c2 --- /dev/null +++ b/services/auth/src/db/poolSingleton.js @@ -0,0 +1,83 @@ +// Coneción Singleton a base de datos. + +import { Pool } from 'pg'; + +class DatabaseCore { + constructor() { + + if (DatabaseCore.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + DatabaseCore.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} +class DatabaseTenants { + constructor() { + + if (DatabaseTenants.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + DatabaseTenants.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} + +// const db = new Database(); +// db.query('SELECT * FROM users'); + +const poolCore = new DatabaseCore(); +const poolTenants = new DatabaseTenants(); +export default {poolCore, poolTenants}; +export { poolCore, poolTenants }; +//export { DatabaseCore, DatabaseTenants }; \ No newline at end of file diff --git a/services/auth/src/index.js b/services/auth/src/index.js deleted file mode 100644 index d596bb8..0000000 --- a/services/auth/src/index.js +++ /dev/null @@ -1,374 +0,0 @@ -// services/auth/src/index.js -// ------------------------------------------------------------ -// SuiteCoffee — Servicio de Autenticación (Express + OIDC) -// - ESM compatible (Node >=18) -// - Sesiones con Redis (compartibles con otros servicios) -// - Vistas EJS (login) -// - Registro de usuario: /auth/api/users/register (DB + Authentik) -// ------------------------------------------------------------ - -import 'dotenv/config'; -import path from 'node:path'; -import fs from 'node:fs/promises'; -import { fileURLToPath } from 'node:url'; -import { Pool } from 'pg'; -import express from 'express'; - -import crypto from 'node:crypto'; -import fetch from "node-fetch"; - -import { createRedisSession } from "../shared/middlewares/redisConnect.js"; - -// ----------------------------------------------------------------------------- -// Variables globales -// ----------------------------------------------------------------------------- -const PORT = process.env.PORT || 4040; -const ISSUER = process.env.AUTHENTIK_ISSUER?.replace(/\/?$/, "/"); // asegura barra final -const CLIENT_ID = process.env.OIDC_CLIENT_ID; -const CLIENT_SECRET = process.env.OIDC_CLIENT_SECRET; -const REDIRECT_URI = process.env.OIDC_REDIRECT_URI || process.env.AUTH_CALLBACK_URL; -const APP_BASE_URL = process.env.APP_BASE_URL || "http://localhost:3030"; - -// ----------------------------------------------------------------------------- -// Utilidades / Helpers -// ----------------------------------------------------------------------------- - - -// ----------------------------------------------------------------------------- -// Utilidades -// ----------------------------------------------------------------------------- -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -function requiredEnv(keys) { - const missing = keys.filter((k) => !process.env[k]); - if (missing.length) { - console.warn(`Falta configurar variables de entorno: ${missing.join(', ')}`); - } -} - - - -// ----------------------------------------------------------------------------- -// Configuración Express -// ----------------------------------------------------------------------------- -const app = express(); -app.set('trust proxy', Number(process.env.TRUST_PROXY_HOPS || 2)); -app.disable("x-powered-by"); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -// Vistas EJS -app.set('views', path.join(__dirname, 'views')); -app.set('view engine', 'ejs'); - -// Archivos estáticos opcionales (ajusta si tu estructura difiere) -app.use(express.static(path.join(__dirname, 'public'))); -app.use('/pages', express.static(path.join(__dirname, 'pages'))); - - -// ----------------------------------------------------------------------------- -// Sesión (Redis) -// ----------------------------------------------------------------------------- -// --- Sesión/Redis --- -const { sessionMw, trustProxy } = await createRedisSession(); -if (trustProxy) app.set("trust proxy", 1); -app.use(sessionMw); -app.use(express.json()); - - -// --- Utiles OIDC --- -function base64url(buf) { -return Buffer.from(buf).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); -} - - -function genPKCE() { -const verifier = base64url(crypto.randomBytes(32)); -const challenge = base64url(crypto.createHash("sha256").update(verifier).digest()); -return { verifier, challenge }; -} - - -function authorizeUrl({ state, challenge }) { -const u = new URL(`${ISSUER}authorize/`); -u.searchParams.set("client_id", CLIENT_ID); -u.searchParams.set("redirect_uri", REDIRECT_URI); -u.searchParams.set("response_type", "code"); -u.searchParams.set("scope", "openid email profile"); -u.searchParams.set("state", state); -u.searchParams.set("code_challenge", challenge); -u.searchParams.set("code_challenge_method", "S256"); -return u.toString(); -} - - -async function exchangeCodeForTokens({ code, verifier }) { -const tokenUrl = `${ISSUER}token/`; -const body = new URLSearchParams({ -grant_type: "authorization_code", -code, -redirect_uri: REDIRECT_URI, -client_id: CLIENT_ID, -code_verifier: verifier, -}); -// auth básica si el proveedor la requiere (Authentik soporta ambos modos) -const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"); -const res = await fetch(tokenUrl, { -method: "POST", -headers: { -"content-type": "application/x-www-form-urlencoded", -"authorization": `Basic ${basic}`, -}, -body, -}); -if (!res.ok) throw new Error(`Token endpoint ${res.status}`); -return res.json(); -} - -// ---------------------------------------------------------- -// Middleware para datos globales -// ---------------------------------------------------------- -app.use((req, res, next) => { - res.locals.currentPath = req.path; - res.locals.pageTitle = "SuiteCoffee"; - res.locals.pageId = ""; - next(); -}); - - -// ----------------------------------------------------------------------------- -// PostgreSQL — DB tenants (usuarios de suitecoffee) -// ----------------------------------------------------------------------------- -const tenantsPool = new Pool({ - host: process.env.TENANTS_HOST || 'dev-tenants', - port: Number(process.env.TENANTS_PORT || 5432), - user: process.env.TENANTS_USER || 'dev-user-postgres', - password: process.env.TENANTS_PASS || 'dev-pass-postgres', - database: process.env.TENANTS_DB || 'dev-postgres', - max: 10, -}); - -// ----------------------------------------------------------------------------- -// PostgreSQL — DB principal (metadatos de negocio) -// ----------------------------------------------------------------------------- -requiredEnv(['DB_HOST', 'DB_USER', 'DB_PASS', 'DB_NAME']); -const mainPool = new Pool({ - host: process.env.DB_HOST || 'dev-db', - port: Number(process.env.DB_PORT || 5432), - user: process.env.DB_USER || 'dev-user-suitecoffee', - password: process.env.DB_PASS || 'dev-pass-suitecoffee', - database: process.env.DB_NAME || 'dev-suitecoffee', - max: 10, - idleTimeoutMillis: 30_000, -}); - -// ---------------------------------------------------------- -// Verificación de conexión -// ---------------------------------------------------------- - -async function verificarConexion() { - try { - console.log(`[AUTH] Comprobando accesibilidad a la db ${process.env.DB_NAME} del host ${process.env.DB_HOST} ...`); - var client = await mainPool.connect(); - var { rows } = await client.query('SELECT NOW() AS ahora'); - console.log(`\n[AUTH] Conexión con ${process.env.DB_NAME} OK. Hora DB:`, rows[0].ahora); - } catch (error) { - console.error('[AUTH] Error al conectar con la base de datos al iniciar:', error.message); - console.error('[AUTH] Revisar DB_HOST/USER/PASS/NAME, accesos de red y firewall.'); - } finally { - client.release(); - } -} - - -// ----------------------------------------------------------------------------- -// Vistas -// ----------------------------------------------------------------------------- - - -// ============================================= -// Registro de usuario (DB principal) -// ============================================= - -requiredEnv(['TENANT_INIT_SQL']); -async function loadInitSqlFromEnv() { - const v = process.env.TENANT_INIT_SQL?.trim(); - if (!v) return ''; - try { - // ¿Es una ruta existente? - const p = path.isAbsolute(v) ? v : path.resolve(__dirname, v); - const txt = await fs.readFile(p, 'utf8'); - console.log(`[TENANT INIT] Cargado desde archivo: ${p} (${txt.length} bytes)`); - return String(txt || ''); - } catch { - // Tratar como literal - console.log(`[TENANT INIT] Usando SQL literal desde TENANT_INIT_SQL (${v.length} chars).`); - return v; - } -} - -// Reemplaza placeholders simples en la plantilla de SQL (opcional) -function renderInitSqlTemplate(sql, { schema, owner }) { - return sql - .replaceAll(':TENANT_SCHEMA', `"${schema}"`) - .replaceAll(':OWNER', `"${owner}"`); -} -// Genera ids sencillos -function newTenantIds() { - return { - tenant_uuid: crypto.randomUUID(), - tenant_role: null, // lo decidirás luego (owner, barman, staff) - }; -} - -async function createTenantUserAndSchema(tenClient, { tenant_uuid, password }) { - const roleName = `tenant_${tenant_uuid.replace(/-/g, '')}`; - const schemaName = `t_${tenant_uuid.replace(/-/g, '')}`; - const escapedPassword = `'${String(password).replace(/'/g, "''")}'`; - - // 1) crear role y schema (misma conexión que ya viene en BEGIN desde la ruta) - await tenClient.query(`CREATE ROLE "${roleName}" LOGIN PASSWORD ${escapedPassword}`); - await tenClient.query(`CREATE SCHEMA "${schemaName}" AUTHORIZATION "${roleName}"`); - await tenClient.query(`GRANT USAGE ON SCHEMA "${schemaName}" TO "${roleName}"`); - await tenClient.query(`ALTER ROLE "${roleName}" INHERIT`); - // (idempotente) - await tenClient.query(`CREATE SCHEMA IF NOT EXISTS "${schemaName}"`); - - // 2) cargar y sanear la plantilla - let sql = await loadInitSqlFromEnv(); - if (!sql?.trim()) { - console.log('[TENANT INIT] No hay SQL de plantilla; se omite.'); - return { roleName, schemaName }; - } - - // 👉 quita metacomandos psql '\' (por si alguno quedó) y cualquier cambio de search_path dentro del dump - sql = sql - .split(/\r?\n/) - .filter(line => !line.trim().startsWith('\\')) // \restrict, \unrestrict, \i, etc. - .filter(line => !/^SET\s+search_path\b/i.test(line)) // SET search_path = ... - .filter(line => !/set_config\(\s*'search_path'/i.test(line)) // SELECT set_config('search_path',... - .join('\n'); - - // si usás placeholders, renderealos acá (opcional) - // sql = renderInitSqlTemplate(sql, { schema: schemaName, owner: roleName }); - - // 3) forzá el search_path SOLO dentro de esta transacción - await tenClient.query(`SET LOCAL search_path TO "${schemaName}", public`); - - // 4) ejecutar el dump (una sola vez, no lo partas por ';' para no romper $$...$$) - await tenClient.query(sql); - - console.log(`[TENANT INIT] OK usuario="${roleName}" schema="${schemaName}"`); - return { roleName, schemaName }; -} - -//============================================= -// ---------- Authentik (API & OIDC) ---------- -//============================================= - - -// =========================== -// GET /auth/users/register -// =========================== - -// =========================== -// POST /auth/login -// =========================== -app.get("/auth/login", (req, res) => { -const { verifier, challenge } = genPKCE(); -const state = base64url(crypto.randomBytes(24)); -req.session.pkce_verifier = verifier; -req.session.oidc_state = state; -const url = authorizeUrl({ state, challenge }); -res.redirect(302, url); -}); -// =========================== -// GET /auth/callback -// =========================== -app.get("/auth/callback", async (req, res) => { -try { -const { code, state } = req.query; -if (!code || !state) return res.status(400).send("Faltan parámetros"); -if (state !== req.session.oidc_state) return res.status(400).send("State inválido"); - - -const verifier = req.session.pkce_verifier; -if (!verifier) return res.status(400).send("PKCE verifier faltante"); - - -const tokens = await exchangeCodeForTokens({ code, verifier }); -// Guarda en sesión (ID Token, Access Token, Refresh Token si viene) -req.session.tokens = { -id_token: tokens.id_token, -access_token: tokens.access_token, -refresh_token: tokens.refresh_token, -token_type: tokens.token_type, -expires_in: tokens.expires_in, -received_at: Date.now(), -}; -// Limpia PKCE/state -delete req.session.pkce_verifier; -delete req.session.oidc_state; - - -// Redirige al home de App -res.redirect(303, `${APP_BASE_URL}/`); -} catch (e) { -console.error("/auth/callback error", e); -res.status(500).send("Error en callback"); -} -}); - - -// =========================== -// POST /auth/logout -// =========================== -app.get("/auth/logout", (req, res) => { -req.session.destroy(() => { -res.clearCookie(process.env.SESSION_COOKIE_NAME || "sc.sid"); -res.redirect(303, APP_BASE_URL || "/"); -}); -}); - - -// ============================================= -// Healthcheck -// ============================================= -app.get('/health', (_req, res) => res.status(200).json({ status: 'ok'})); - -// ============================================= -// 404 + Manejo de errores -// ============================================= -app.use((req, res) => res.status(404).json({ error: 'Error 404, No se encontró la página', path: req.originalUrl })); - -app.use((err, _req, res, _next) => { - console.error('[AUTH] ', err); - if (res.headersSent) return; - res.status(500).json({ error: '¡Oh! A ocurrido un error en el servidor auth.', detail: err.stack || String(err) }); -}); - -/* ------------------------------------------------------------------------------ -Exportación principal del módulo. -Es típico exportar la instancia (app) y arrancarla en otro archivo. -- Facilita tests (p.ej. con supertest: import app from './app.js') -- Evita que el servidor se inicie al importar el módulo. - -# Default - export default app; // importar: import app from './app.js' - -# Con nombre - export const app = express(); // importar: import { app } from './app.js' ------------------------------------------------------------------------------ -*/ -export default app; - -// ----------------------------------------------------------------------------- -// Arranque -// ----------------------------------------------------------------------------- -app.listen(PORT, () => { - console.log(`[AUTH] Servicio de autenticación escuchando en http://localhost:${PORT}`); - verificarConexion(); - // OIDCdiscover(); -}); \ No newline at end of file diff --git a/services/auth/src/index.mjs b/services/auth/src/index.mjs new file mode 100644 index 0000000..3f3442d --- /dev/null +++ b/services/auth/src/index.mjs @@ -0,0 +1,240 @@ +// services/auth/src/index.js +// ------------------------------------------------------------ +// SuiteCoffee — Servicio de Autenticación (Express + OIDC) +// ------------------------------------------------------------ + +import 'dotenv/config'; +import express from 'express'; // Framework para enderizado de apps Web +import expressLayouts from 'express-ejs-layouts'; +// import { poolCore, poolTenants } from '@suitecoffee/db'; // dbCore y dbTenants desde módulo +import { poolCore, poolTenants } from '#db'; // dbCore y dbTenants +import v1Router from '#v1Router'; // Rutas API v1 +import expressPages from '#pages'; // Rutas "/", "/dashboard", ... + +import path from 'path'; +import { fileURLToPath } from 'url'; // Converts a file:// URL string or URL object into a platform-specific file +import cookieParser from 'cookie-parser'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +import fs from 'node:fs/promises'; +import crypto from 'node:crypto'; +import fetch from "node-fetch"; + + +// ----------------------------------------------------------------------------- +// Validación de entorno mínimo (ajusta nombres si difieren) +// ----------------------------------------------------------------------------- +// Función para verificar que ciertas variables de entorno estén definida +function checkRequiredEnvVars(...requiredKeys) { + const missingKeys = requiredKeys.filter((key) => !process.env[key]); // Filtramos las que NO existen en process.env + if (missingKeys.length > 0) { // Si falta alguna, mostramos una advertencia + console.warn( + `[APP] No se encontraron las siguientes variables de entorno: \n\n-> ${missingKeys.join('\n-> ')}`+ + `\n` + ); + } +} + +checkRequiredEnvVars( + 'PORT', 'APP_BASE_URL', + 'CORE_DB_HOST', 'CORE_DB_PORT', 'CORE_DB_NAME', + 'TENANTS_DB_HOST', 'TENANTS_DB_PORT', 'TENANTS_DB_NAME', + + 'OIDC_LOGIN_URL', 'OIDC_REDIRECT_URI', + 'OIDC_CLIEN_ID', 'OIDC_CONFIG_URL', 'OIDC_ISSUER', + 'OIDC_ISSUER_DISCOVERY', 'OIDC_AUTHORIZE_URL', 'OIDC_TOKEN_URL', + 'OIDC_USERINFO_URL', 'OIDC_LOGOUT_URL', 'OIDC_JWKS_URL', + + 'SESSION_SECRET', 'SESSION_COOKIE_NAME', + 'AK_REDIS_URL', 'AK_TOKEN' +); + + + + + +// ---------------------------------------------------------- +// Variables del sistema +// ---------------------------------------------------------- + +// De entorno +const PORT = process.env.PORT; +const APP_BASE_URL = process.env.APP_BASE_URL; + +const CORE_DB_HOST = process.env.CORE_DB_HOST; +const CORE_DB_PORT = process.env.CORE_DB_PORT; +const CORE_DB_NAME = process.env.CORE_DB_NAME; + +const TENANTS_DB_HOST = process.env.TENANTS_DB_HOST; +const TENANTS_DB_PORT = process.env.TENANTS_DB_PORT; +const TENANTS_DB_NAME = process.env.TENANTS_DB_NAME; + +const OIDC_LOGIN_URL = process.env.OIDC_LOGIN_URL; +const OIDC_REDIRECT_URI = process.env.OIDC_REDIRECT_URI; + +const OIDC_CLIEN_ID = process.env.OIDC_CLIEN_ID; +const OIDC_CONFIG_URL = process.env.OIDC_CONFIG_URL; +const OIDC_ISSUER = process.env.OIDC_ISSUER; +const OIDC_ISSUER_DISCOVERY = process.env.OIDC_ISSUER_DISCOVERY; +const OIDC_AUTHORIZE_URL = process.env.OIDC_AUTHORIZE_URL; +const OIDC_TOKEN_URL = process.env.OIDC_TOKEN_URL; +const OIDC_USERINFO_URL = process.env.OIDC_USERINFO_URL; +const OIDC_LOGOUT_URL = process.env.OIDC_LOGOUT_URL; +const OIDC_JWKS_URL = process.env.OIDC_JWKS_URL; + +const AK_SESSION_SECRET = process.env.AK_SESSION_SECRET; +const AK_SESSION_COOKIE_NAME = process.env.AK_SESSION_COOKIE_NAME; +const AK_REDIS_URL = process.env.AK_REDIS_URL; + + + +// ----------------------------------------------------------------------------- +// Utilidades / Helpers +// ----------------------------------------------------------------------------- + + + + + +// ----------------------------------------------------------------------------- +// Configuración Express +// ----------------------------------------------------------------------------- + +const app = express(); +app.set('trust proxy', true); +app.set("views", path.join(__dirname, "views")); +app.set("view engine", "ejs"); +app.set("layout", "layouts/main"); +app.disable("x-powered-by"); + +app.use(express.json()); +app.use(express.json({ limit: '1mb' })); +app.use(express.urlencoded({ extended: true })); +// Archivos estáticos que fuerzan la re-descarga de arhivos +app.use(express.static(path.join(__dirname, "public"), { + etag: false, maxAge: 0, + setHeaders: (res, path) => { + res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + } +})); +app.use(cookieParser(process.env.SESSION_SECRET)); +app.use(expressPages); // Renderizado trae las paginas desde ./services/manso/src/routes/routes.js + + + + + + + +// ---------------------------------------------------------- +// Middleware para datos globales +// ---------------------------------------------------------- +app.use((req, res, next) => { + res.locals.currentPath = req.path; + res.locals.pageTitle = "SuiteCoffee"; + res.locals.pageId = ""; + next(); +}); + + + +// ---------------------------------------------------------- +// Verificación de conexión +// ---------------------------------------------------------- + +async function verificarConexionCore() { + try { + console.log(`[APP] Comprobando accesibilidad a la db ${CORE_DB_NAME} del host ${CORE_DB_HOST} ...`); + const client = await poolCore.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[APP] Conexión con ${CORE_DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[APP] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[APP] Revisar credenciales, accesos de red y firewall.'); + } +} +async function verificarConexionTenants() { + try { + console.log(`[APP] Comprobando accesibilidad a la db ${TENANTS_DB_NAME} del host ${TENANTS_DB_HOST} ...`); + const client = await poolTenants.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[APP] Conexión con ${TENANTS_DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[APP] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[APP] Revisar credenciales, accesos de red y firewall.'); + } +} + + + +// ============================================= +// Registro de usuario (DB principal) +// ============================================= + + + + +//============================================= +// ---------- Authentik (API & OIDC) ---------- +//============================================= + + +// =========================== +// GET /auth/users/register +// =========================== + +// =========================== +// POST /auth/login +// =========================== + +app.get("/auth/login", (req, res) => { + const { verifier, challenge } = genPKCE(); + const state = base64url(crypto.randomBytes(24)); + req.session.pkce_verifier = verifier; + req.session.oidc_state = state; + const url = authorizeUrl({ state, challenge }); + res.redirect(302, url); +}); + + +// =========================== +// GET /auth/callback +// =========================== + + +// ----------------------------------------------------------------------------- +// Healthcheck +// ----------------------------------------------------------------------------- +app.get('/health', (_req, res) => { + res.status(200).json({ status: 'ok'}), + console.log(`[AUTH] Saludable`) +}); + + + +// ============================================= +// 404 + Manejo de errores +// ============================================= +app.use((req, res) => res.status(404).json({ error: 'Error 404, No se encontró la página', path: req.originalUrl })); + +app.use((err, _req, res, _next) => { + console.error('[AUTH] ', err); + if (res.headersSent) return; + res.status(500).json({ error: '¡Oh! A ocurrido un error en el servidor auth.', detail: err.stack || String(err) }); +}); + + + + +// ----------------------------------------------------------------------------- +// Arranque +// ----------------------------------------------------------------------------- +app.listen(PORT, () => { + console.log(`[AUTH] Servicio de autenticación escuchando en http://localhost:${PORT}`); + verificarConexionCore(); + verificarConexionTenants(); +}); \ No newline at end of file diff --git a/services/auth/src/pages/index.html b/services/auth/src/pages/index.html.bak similarity index 100% rename from services/auth/src/pages/index.html rename to services/auth/src/pages/index.html.bak diff --git a/services/auth/src/pages/pages.js b/services/auth/src/pages/pages.js new file mode 100644 index 0000000..bbf228c --- /dev/null +++ b/services/auth/src/pages/pages.js @@ -0,0 +1,20 @@ +// services/manso/src/api/v1/routes/routes.js + +import { Router } from 'express'; + +const router = Router(); + +// ---------------------------------------------------------- +// Rutas de UI +// ---------------------------------------------------------- + +/*router.get('/', (req, res) => { + res.locals.pageTitle = "Inicio"; // Título de pestaña + res.locals.pageId = "home"; // Sidebar contextual + res.render("dashboard"); // Archivo .ejs a renderizar + // res.json({ ok: true, route: '/inicio' }); // Debug json +});*/ + + + +export default router; \ No newline at end of file diff --git a/services/manso/.env.development b/services/manso/.env.development index 653f03e..93453b3 100644 --- a/services/manso/.env.development +++ b/services/manso/.env.development @@ -1,6 +1,6 @@ NODE_ENV=development -PORT=3030 +PORT=1010 DB_HOST=dev-tenants DB_NAME=manso diff --git a/services/manso/package-lock.json b/services/manso/package-lock.json index 62c66bd..3b7c347 100644 --- a/services/manso/package-lock.json +++ b/services/manso/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "chalk": "^5.6.0", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", "dotenv": "^17.2.1", "ejs": "^3.1.10", @@ -239,6 +240,25 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", diff --git a/services/manso/package.json b/services/manso/package.json index aac4b30..94aa1c1 100644 --- a/services/manso/package.json +++ b/services/manso/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "chalk": "^5.6.0", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", "dotenv": "^17.2.1", "ejs": "^3.1.10", diff --git a/services/manso/src/index.mjs b/services/manso/src/index.mjs index 4df8237..b264a5c 100644 --- a/services/manso/src/index.mjs +++ b/services/manso/src/index.mjs @@ -1,8 +1,10 @@ // ./services/manso/src/index.mjs +// ------------------------------------------------------------ +// MVP Manso de SuiteCoffee — Aplicación MVP +// ------------------------------------------------------------ -import 'dotenv/config';// Variables de Entorno - -import favicon from 'serve-favicon'; // Favicon +import 'dotenv/config'; // Variables de Entorno +import favicon from 'serve-favicon'; // Favicon import express from 'express'; import expressLayouts from 'express-ejs-layouts'; @@ -13,10 +15,34 @@ import expressPages from '#pages'; // Rutas "/", "/dashboard", ... import path from 'path'; import { fileURLToPath } from 'url'; +import cookieParser from 'cookie-parser'; + const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +// ----------------------------------------------------------------------------- +// Validación de entorno mínimo (ajusta nombres si difieren) +// ----------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + // ---------------------------------------------------------- // Variables del sistema @@ -39,9 +65,13 @@ app.set("layout", "layouts/main"); app.use(express.json()); app.use(express.json({ limit: '1mb' })); -app.use(express.static(path.join(__dirname, 'public'))); // Carga de archivos estaticos -app.use(expressLayouts); // Carga los layouts que usara el renderizado +app.use(express.urlencoded({ extended: true })); app.use(favicon(path.join(__dirname, 'public', 'favicon', 'favicon.ico'), {maxAge: '1y'})); +app.use(express.static(path.join(__dirname, 'public'))); // Carga de archivos estaticos + + +app.use(expressLayouts); // Carga los layouts que usara el renderizado +app.use(cookieParser(process.env.SESSION_SECRET)); app.use(expressPages); // Renderizado trae las paginas desde ./services/manso/src/routes/routes.js @@ -291,31 +321,77 @@ app.post('/api/table/:table', async (req, res) => { // ---------------------------------------------------------- // Verificación de conexión // ---------------------------------------------------------- + async function verificarConexion() { + try { + console.log(`[Manso] Comprobando accesibilidad a la db ${process.env.DB_NAME} ...`); + const client = await pool.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[Manso] Conexión con ${process.env.DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[Manso] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[Manso] Revisar credenciales, accesos de red y firewall.'); + } +} +/*async function verificarConexion() { try { var client = await pool.getClient(); let res = await client.query('SELECT NOW() AS hora'); - console.log(`\nConexión con la base de datos ${process.env.DB_NAME} fue exitosa.`); - console.log('Fecha y hora actual de la base de datos:\n ->', res.rows[0].hora); + console.log(`\n[Manso] Conexión con la base de datos ${process.env.DB_NAME} fue exitosa.`); + console.log('[Manso] Fecha y hora actual de la base de datos:\n ->', res.rows[0].hora); } catch (error) { - console.error('Error al conectar con la base de datos al iniciar: \n ->', error.message); - console.error('Revisar credenciales y accesos de red.'); + console.error('[Manso] Error al conectar con la base de datos al iniciar: \n ->', error.message); + console.error('[Manso] Revisar credenciales y accesos de red.'); } finally{ client.release(); } -} +}*/ + + + + + + + + + + + + + + + + +// ----------------------------------------------------------------------------- +// 404 + Manejo de errores +// ----------------------------------------------------------------------------- + +app.use((req, res) => res.status(404).json({ error: 'Error 404, No se encontró la página', path: req.originalUrl })); + +app.use((err, _req, res, _next) => { + console.error('[APP] ', err); + if (res.headersSent) return; + res.status(500).json({ error: '¡Oh! A ocurrido un error en el servidor app.', detail: err.stack || String(err) }); +}); + // ---------------------------------------------------------- // Inicio del servidor // ---------------------------------------------------------- app.listen(PORT, () => { - console.log(`Servidor de aplicación escuchando en ${`http://localhost:${PORT}`}`); - console.log(`Comprobando accesibilidad a la db ${process.env.DB_NAME} del host ${process.env.DB_HOST} ...`); + console.log(`[Manso] Servidor de aplicación escuchando en ${`http://localhost:${PORT}`}`); + console.log(`[Manso] Comprobando accesibilidad a la db ${process.env.DB_NAME} del host ${process.env.DB_HOST} ...`); verificarConexion(); }); +// ----------------------------------------------------------------------------- // Healthcheck -app.get('/health', (_req, res) => res.status(200).json({ status: 'ok'})); +// ----------------------------------------------------------------------------- +app.get('/health', (_req, res) => { + res.status(200).json({ status: 'ok'}), + console.log(`[Manso] Saludable`) +}); \ No newline at end of file diff --git a/services/manso/src/views/reportes.ejs b/services/manso/src/views/reportes.ejs index 96640e8..c936d42 100644 --- a/services/manso/src/views/reportes.ejs +++ b/services/manso/src/views/reportes.ejs @@ -469,7 +469,7 @@ async function loadAsist(){ const key = `${r.documento}::${r.nombre||''}::${r.apellido||''}`; if (!byKey.has(key)) byKey.set(key, { doc:r.documento, nombre:r.nombre||'', apellido:r.apellido||'', rows:[] }); byKey.get(key).rows.push({ - fecha: fStr, + fecha: fStr, horas: Number(r.horas_dia ?? r.horas ?? (r.minutos_dia||0)/60), pares: Number(r.pares_dia ?? r.pares ?? 0) }); diff --git a/services/plugins/.env.development b/services/plugins/.env.development new file mode 100644 index 0000000..0b8c0aa --- /dev/null +++ b/services/plugins/.env.development @@ -0,0 +1,62 @@ +# ===== Runtime ===== +NODE_ENV=development +PORT=5050 + + +# ===== Session (usa el Redis del stack) ===== +# Para DEV podemos reutilizar el Redis de Authentik. En prod conviene uno separado. +SESSION_SECRET=Neon*Mammal*Boaster*Ludicrous*Fender8*Crablike +SESSION_COOKIE_NAME=sc.sid + +# ===== DB principal (metadatos de SuiteCoffee) ===== +# Usa el alias de red del servicio 'db' (compose: aliases [dev-db]) +DB_HOST=dev-db +DB_NAME=dev_suitecoffee_core +DB_PORT=5432 +DB_USER=dev-user-suitecoffee +DB_PASS=dev-pass-suitecoffee + +CORE_DB_HOST=dev-db +CORE_DB_NAME=dev_suitecoffee_core +CORE_DB_PORT=5432 +CORE_DB_USER=dev-user-suitecoffee +CORE_DB_PASS=dev-pass-suitecoffee + +# ===== DB tenants (Tenants de SuiteCoffee) ===== +TENANTS_HOST=dev-tenants +TENANTS_DB=dev_suitecoffee_tenants +TENANTS_PORT=5432 +TENANTS_USER=suitecoffee +TENANTS_PASS=suitecoffee + +TENANTS_DB_HOST=dev-tenants +TENANTS_DB_NAME=dev_suitecoffee_tenants +TENANTS_DB_PORT=5432 +TENANTS_DB_USER=suitecoffee +TENANTS_DB_PASS=suitecoffee + + +# ===== Authentik — Admin API (server-to-server dentro de la red) ===== +# Usa el alias de red del servicio 'authentik' y su puerto interno 9000 +AK_TOKEN=h2apVHbd3ApMcnnSwfQPXbvximkvP8HnUE25ot3zXWuEEtJFaNCcOzDHB6Xw +AK_REDIS_URL=redis://ak-redis:6379 + +# ===== OIDC (DEBE coincidir con el Provider) ===== +# DEV (todo dentro de la red de Docker): +# - El auth service redirige al navegador a este issuer. Si NO tenés reverse proxy hacia Authentik, +# esta URL interna NO será accesible desde el navegador del host. En ese caso, ver nota más abajo. + +APP_BASE_URL=https://suitecoffee.uy + +OIDC_LOGIN_URL=https://sso.suitecoffee.uy +OIDC_REDIRECT_URI = https://suitecoffee.uy/auth/callback + +OIDC_CLIEN_ID=1orMM8vOvf3WkN2FejXYvUFpPtONG0Lx1eMlwIpW +OIDC_CONFIG_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/.well-known/openid-configuration +OIDC_ISSUER=https://sso.suitecoffee.uy/application/o/suitecoffee/ +OIDC_ISSUER_DISCOVERY=https://sso.suitecoffee.uy/application/o/suitecoffee/.well-known/openid-configuration +OIDC_AUTHORIZE_URL=https://sso.suitecoffee.uy/application/o/authorize/ +OIDC_TOKEN_URL=https://sso.suitecoffee.uy/application/o/token/ +OIDC_USERINFO_URL=https://sso.suitecoffee.uy/application/o/userinfo/ +OIDC_LOGOUT_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/end-session/ +OIDC_JWKS_URL=https://sso.suitecoffee.uy/application/o/suitecoffee/jwks/ \ No newline at end of file diff --git a/services/plugins/.env.production b/services/plugins/.env.production new file mode 100644 index 0000000..e69de29 diff --git a/services/plugins/package-lock.json b/services/plugins/package-lock.json new file mode 100644 index 0000000..8051c8f --- /dev/null +++ b/services/plugins/package-lock.json @@ -0,0 +1,2381 @@ +{ + "name": "plugins", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugins", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "bcrypt": "^6.0.0", + "chalk": "^5.6.0", + "connect-redis": "^9.0.0", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^17.2.1", + "ejs": "^3.1.10", + "express": "^5.1.0", + "express-ejs-layouts": "^2.5.1", + "express-session": "^1.18.2", + "ioredis": "^5.7.0", + "jose": "^6.1.0", + "jsonwebtoken": "^9.0.2", + "jwks-rsa": "^3.2.0", + "morgan": "^1.10.1", + "node-appwrite": "^20.2.1", + "node-fetch": "^3.3.2", + "pg": "^8.16.3", + "pg-format": "^1.0.4", + "redis": "^5.8.2", + "serve-favicon": "^2.5.1" + }, + "devDependencies": { + "cross-env": "^10.0.0", + "nodemon": "^3.1.10" + } + }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ioredis/commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", + "license": "MIT" + }, + "node_modules/@redis/bloom": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.3.tgz", + "integrity": "sha512-1eldTzHvdW3Oi0TReb8m1yiFt8ZwyF6rv1NpZyG5R4TpCwuAdKQetBKoCw7D96tNFgsVVd6eL+NaGZZCqhRg4g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@redis/client": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.3.tgz", + "integrity": "sha512-MZVUE+l7LmMIYlIjubPosruJ9ltSLGFmJqsXApTqPLyHLjsJUSAbAJb/A3N34fEqean4ddiDkdWzNu4ZKPvRUg==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.3.tgz", + "integrity": "sha512-DRR09fy/u8gynHGJ4gzXYeM7D8nlS6EMv5o+h20ndTJiAc7RGR01fdk2FNjnn1Nz5PjgGGownF+s72bYG4nZKQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@redis/search": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.3.tgz", + "integrity": "sha512-EMIvEeGRR2I0BJEz4PV88DyCuPmMT1rDtznlsHY3cKSDcc9vj0Q411jUnX0iU2vVowUgWn/cpySKjpXdZ8m+5g==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@redis/time-series": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.3.tgz", + "integrity": "sha512-5Jwy3ilsUYQjzpE7WZ1lEeG1RkqQ5kHtwV1p8yxXHSEmyUbC/T/AVgyjMcm52Olj/Ov/mhDKjx6ndYUi14bXsw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.8.3" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", + "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.1.tgz", + "integrity": "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.14.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz", + "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz", + "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect-redis": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-9.0.0.tgz", + "integrity": "sha512-QwzyvUePTMvEzG1hy45gZYw3X3YHrjmEdSkayURlcZft7hqadQ3X39wYkmCqblK2rGlw+XItELYt6GnyG6DEIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "express-session": ">=1", + "redis": ">=5" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-ejs-layouts": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/express-ejs-layouts/-/express-ejs-layouts-2.5.1.tgz", + "integrity": "sha512-IXROv9n3xKga7FowT06n1Qn927JR8ZWDn5Dc9CJQoiiaaDqbhW5PDmWShzbpAa2wjWT1vJqaIM1S6vJwwX11gA==" + }, + "node_modules/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ioredis": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.8.1.tgz", + "integrity": "sha512-Qho8TgIamqEPdgiMadJwzRMW3TudIg6vpg4YONokGDudy4eqRIJtDbVX72pfLBcWxvbn3qm/40TyGUObdW4tLQ==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.4.0", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jose": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz", + "integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", + "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.20", + "@types/jsonwebtoken": "^9.0.4", + "debug": "^4.3.4", + "jose": "^4.15.4", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/morgan": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-appwrite": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-20.2.1.tgz", + "integrity": "sha512-RweIh+3RHjprsxhWaJzcQr/UDMBMsZCma50TIJ9t3onVgs5jAT9aqFnsMlaaC9QZn1sXpPUQV90W6uvtm64DnQ==", + "license": "BSD-3-Clause", + "dependencies": { + "node-fetch-native-with-agent": "1.7.2" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-fetch-native-with-agent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-fetch-native-with-agent/-/node-fetch-native-with-agent-1.7.2.tgz", + "integrity": "sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g==", + "license": "MIT" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-format": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz", + "integrity": "sha512-YyKEF78pEA6wwTAqOUaHIN/rWpfzzIuMh9KdAhc3rSLQ/7zkRFcCgYBAEGatDstLyZw4g0s9SNICmaTGnBVeyw==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redis": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.8.3.tgz", + "integrity": "sha512-MfSrfV6+tEfTw8c4W0yFp6XWX8Il4laGU7Bx4kvW4uiYM1AuZ3KGqEGt1LdQHeD1nEyLpIWetZ/SpY3kkbgrYw==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.8.3", + "@redis/client": "5.8.3", + "@redis/json": "5.8.3", + "@redis/search": "5.8.3", + "@redis/time-series": "5.8.3" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-favicon": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", + "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", + "license": "MIT", + "dependencies": { + "etag": "~1.8.1", + "fresh": "~0.5.2", + "ms": "~2.1.3", + "parseurl": "~1.3.2", + "safe-buffer": "~5.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-favicon/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", + "integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + } + } +} diff --git a/services/plugins/package.json b/services/plugins/package.json new file mode 100644 index 0000000..700f3d9 --- /dev/null +++ b/services/plugins/package.json @@ -0,0 +1,47 @@ +{ + "name": "plugins", + "version": "1.0.0", + "main": "src/index.mjs", + "scripts": { + "start": "NODE_ENV=production node ./src/index.js", + "dev": "NODE_ENV=development npx nodemon ./src/index.js", + "test": "NODE_ENV=stage node ./src/index.js" + }, + "author": "Mateo Saldain", + "license": "ISC", + "type": "module", + "devDependencies": { + "cross-env": "^10.0.0", + "nodemon": "^3.1.10" + }, + "dependencies": { + "bcrypt": "^6.0.0", + "chalk": "^5.6.0", + "connect-redis": "^9.0.0", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^17.2.1", + "ejs": "^3.1.10", + "express": "^5.1.0", + "express-ejs-layouts": "^2.5.1", + "express-session": "^1.18.2", + "ioredis": "^5.7.0", + "jose": "^6.1.0", + "jsonwebtoken": "^9.0.2", + "jwks-rsa": "^3.2.0", + "morgan": "^1.10.1", + "node-appwrite": "^20.2.1", + "node-fetch": "^3.3.2", + "pg": "^8.16.3", + "pg-format": "^1.0.4", + "redis": "^5.8.2", + "serve-favicon": "^2.5.1" + }, + "imports": { + "#v1Router": "./src/api/v1/routes/routes.js", + "#pages": "./src/pages/pages.js", + "#db": "./src/db/poolSingleton.js" + }, + "keywords": [], + "description": "" +} diff --git a/services/plugins/src/db/poolSingleton.js b/services/plugins/src/db/poolSingleton.js new file mode 100644 index 0000000..721722f --- /dev/null +++ b/services/plugins/src/db/poolSingleton.js @@ -0,0 +1,82 @@ +// Coneción Singleton a base de datos. + +import { Pool } from 'pg'; + +class DatabaseCore { + constructor() { + + if (DatabaseCore.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + DatabaseCore.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} +class DatabaseTenants { + constructor() { + + if (DatabaseTenants.instance) { + return Database.instance; + } + + const config = { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + port: process.env.DB_LOCAL_PORT ? Number(process.env.DB_LOCAL_PORT) : undefined, + ssl: process.env.PGSSL === 'true' ? { rejectUnauthorized: false } : undefined, + }; + + this.connection = new Pool(config); + + DatabaseTenants.instance = this; + } + async query(sql, params) { + return this.connection.query(sql,params); + } + + async connect() { /* Definida solo para evitar errores */ + return this.connection.connect(); + } + async getClient() { + return this.connection.connect(); + } + + async release() { + await this.connection.end(); + } +} + +// const db = new Database(); +// db.query('SELECT * FROM users'); + +const poolCore = new DatabaseCore(); +const poolTenants = new DatabaseTenants(); +export default {poolCore, poolTenants}; +export { poolCore, poolTenants }; \ No newline at end of file diff --git a/services/plugins/src/index.mjs b/services/plugins/src/index.mjs new file mode 100644 index 0000000..365a882 --- /dev/null +++ b/services/plugins/src/index.mjs @@ -0,0 +1,140 @@ +// services/plugins/asistencias/index.mjs + +// ------------------------------------------------------------ + +// ------------------------------------------------------------ + + +import 'dotenv/config'; // Variables de Entorno +import express from 'express'; +import expressLayouts from 'express-ejs-layouts'; + +import { poolCore, poolTenants } from '#db'; // dbCore y dbTenants + +import path from 'path'; +import { fileURLToPath } from 'url'; +import cookieParser from 'cookie-parser'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + + +// ----------------------------------------------------------------------------- +// Validación de entorno mínimo (ajusta nombres si difieren) +// ----------------------------------------------------------------------------- + +// Función para verificar que ciertas variables de entorno estén definida +function checkRequiredEnvVars(...requiredKeys) { + const missingKeys = requiredKeys.filter((key) => !process.env[key]); // Filtramos las que NO existen en process.env + if (missingKeys.length > 0) { // Si falta alguna, mostramos una advertencia + console.warn( + `[PLUGIN] No se encontraron las siguientes variables de entorno: \n\n-> ${missingKeys.join('\n-> ')}`+ + `\n` + ); + } +} + +checkRequiredEnvVars( + 'PORT', + 'CORE_DB_HOST', 'CORE_DB_PORT', 'CORE_DB_NAME', + 'TENANTS_DB_HOST', 'TENANTS_DB_PORT', 'TENANTS_DB_NAME' +); + +// ---------------------------------------------------------- +// Variables del sistema +// ---------------------------------------------------------- +const PORT = process.env.PORT; + +const CORE_DB_HOST = process.env.CORE_DB_HOST; +const CORE_DB_PORT = process.env.CORE_DB_PORT; +const CORE_DB_NAME = process.env.CORE_DB_NAME; + +const TENANTS_DB_HOST = process.env.TENANTS_DB_HOST; +const TENANTS_DB_PORT = process.env.TENANTS_DB_PORT; +const TENANTS_DB_NAME = process.env.TENANTS_DB_NAME; + +// ---------------------------------------------------------- +// App + Motor de vistas EJS +// ---------------------------------------------------------- +const app = express(); +app.set('trust proxy', true); +app.set("views", path.join(__dirname, "views")); +app.set("view engine", "ejs"); +app.set("layout", "layouts/main"); + +app.use(express.json()); +app.use(express.json({ limit: '1mb' })); +app.use(express.urlencoded({ extended: true })); +app.use(express.static(path.join(__dirname, 'public'))); // Carga de archivos estaticos + + +app.use(expressLayouts); // Carga los layouts que usara el renderizado +app.use(cookieParser(process.env.SESSION_SECRET)); + + +// ---------------------------------------------------------- +// Verificación de conexión +// ---------------------------------------------------------- + +async function verificarConexionCore() { + try { + console.log(`[PLUGINS] Comprobando accesibilidad a la db ${CORE_DB_NAME} del host ${CORE_DB_HOST} ...`); + const client = await poolCore.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[PLUGINS] Conexión con ${CORE_DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[PLUGINS] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[PLUGINS] Revisar credenciales, accesos de red y firewall.'); + } +} +async function verificarConexionTenants() { + try { + console.log(`[PLUGINS] Comprobando accesibilidad a la db ${TENANTS_DB_NAME} del host ${TENANTS_DB_HOST} ...`); + const client = await poolTenants.connect(); + const { rows } = await client.query('SELECT NOW() AS ahora'); + console.log(`\n[PLUGINS] Conexión con ${TENANTS_DB_NAME} OK. Hora DB:`, rows[0].ahora); + client.release(); + } catch (error) { + console.error('[PLUGINS] Error al conectar con la base de datos al iniciar:', error.message); + console.error('[PLUGINS] Revisar credenciales, accesos de red y firewall.'); + } +} + + + + + + +// ---------------------------------------------------------- +// Middleware para datos globales +// ---------------------------------------------------------- +app.use((req, res, next) => { + res.locals.currentPath = req.path; + res.locals.pageTitle = "SuiteCoffee"; + res.locals.pageId = ""; + next(); +}); + + + + +// ---------------------------------------------------------- +// Inicio del servidor +// ---------------------------------------------------------- + +app.listen(PORT, () => { + console.log(`[PLUGINS] http://localhost:${PORT}`); + verificarConexionCore(); + verificarConexionTenants(); +}); + + + +// ----------------------------------------------------------------------------- +// Healthcheck +// ----------------------------------------------------------------------------- +app.get('/health', (_req, res) => { + res.status(200).json({ status: 'ok'}), + console.log(`[PLUGINS] Saludable`) +}); \ No newline at end of file diff --git a/services/shared/middlewares/comandas.html.bak b/services/shared/middlewares/comandas.html.bak deleted file mode 100644 index 93c6e5d..0000000 --- a/services/shared/middlewares/comandas.html.bak +++ /dev/null @@ -1,355 +0,0 @@ - - - - - - Comandas - - - -
-

📋 Nueva Comanda

-
- /api/* -
- -
- -
-
- Productos -
-
- 0 ítems -
-
-
- -
- -
-
-
- - -
-
Detalles
-
-
-
- - -
-
- - -
-
- -
- - -
- -
La fecha se completa automáticamente y los estados/activos usan sus valores por defecto.
- -
-
Carrito
-
-
Aún no agregaste productos.
-
- -
- -
-
-
-
- - - - diff --git a/services/shared/middlewares/dashboard.html.bak b/services/shared/middlewares/dashboard.html.bak deleted file mode 100644 index 78fbbb6..0000000 --- a/services/shared/middlewares/dashboard.html.bak +++ /dev/null @@ -1,293 +0,0 @@ - - - - - - Dashboard - - - -
-

Dashboard

-
- /api/* -
- -
-
- - -
-
- - -
-
- -
- - - -
-
-
Mostrando hasta 100 filas.
-
-
- - - -
- Endpoints -
GET /api/tables • GET /api/schema/:tabla • GET /api/table/:tabla?limit=100 • POST /api/table/:tabla
-
-
- - - - diff --git a/services/shared/middlewares/estadoComandas.html.bak b/services/shared/middlewares/estadoComandas.html.bak deleted file mode 100644 index dd69c9a..0000000 --- a/services/shared/middlewares/estadoComandas.html.bak +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - - Estado de Comandas - - - -
-

🧾 Estado de Comandas

-
- ➕ Nueva comanda -
- -
- -
-
- Listado -
- -
-
-
- - -
-
-
-
- - -
-
- Detalle -
- -
-
-
Selecciona una comanda para ver el detalle.
-
- -
-
-
-
-
- - - - diff --git a/services/shared/middlewares/redisConnect.js b/services/shared/middlewares/redisConnect.js deleted file mode 100644 index 378cf11..0000000 --- a/services/shared/middlewares/redisConnect.js +++ /dev/null @@ -1,58 +0,0 @@ -import session from "express-session"; -import { createClient } from "redis"; - - -export async function createRedisSession({ -redisUrl = process.env.REDIS_URL, -cookieName = process.env.SESSION_COOKIE_NAME || "sc.sid", -secret = process.env.SESSION_SECRET, -trustProxy = process.env.TRUST_PROXY === "1", -ttlSeconds = 60 * 60 * 12, // 12h -} = {}) { -if (!redisUrl) throw new Error("REDIS_URL no definido"); -if (!secret) throw new Error("SESSION_SECRET no definido"); - - -const redis = createClient({ url: redisUrl }); -redis.on("error", (err) => console.error("[Redis]", err)); -await redis.connect(); -console.log("[Redis] conectado"); - - -// Resolver RedisStore (v5 / v6 / v7) -async function resolveRedisStore() { -const mod = await import("connect-redis"); -// v6/v7: named export class -if (typeof mod.RedisStore === "function") return mod.RedisStore; -// v5: default factory connectRedis(session) -if (typeof mod.default === "function") { -const maybe = mod.default; -if (maybe.prototype && (maybe.prototype.get || maybe.prototype.set)) return maybe; // clase -const factory = mod.default(session); -return factory; -} -throw new Error("No se pudo resolver RedisStore de connect-redis"); -} - - -const RedisStore = await resolveRedisStore(); -const store = new RedisStore({ client: redis, prefix: "sc:sess:", ttl: ttlSeconds }); - - -const sessionMw = session({ -name: cookieName, -secret, -resave: false, -saveUninitialized: false, -store, -cookie: { -httpOnly: true, -sameSite: "lax", -secure: process.env.NODE_ENV === "production", // requiere https -maxAge: ttlSeconds * 1000, -}, -}); - - -return { sessionMw, redis, store, trustProxy }; -} \ No newline at end of file