@consenti/api 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +917 -207
  2. package/dist/chunk-IY4L6MNB.js +2 -0
  3. package/dist/chunk-IY4L6MNB.js.map +1 -0
  4. package/dist/chunk-O5UG4OYI.js +2 -0
  5. package/dist/chunk-O5UG4OYI.js.map +1 -0
  6. package/dist/dashboard/assets/ApiConfig-CE-Noelz.js +1 -0
  7. package/dist/dashboard/assets/ArchivedProfileList-Bw-fx-BD.js +1 -0
  8. package/dist/dashboard/assets/AuditLog-D6HiTQ1o.js +1 -0
  9. package/dist/dashboard/assets/ChangePassword-D2rPkzD1.js +1 -0
  10. package/dist/dashboard/assets/ConfirmDialog-DTtw7VJ-.js +1 -0
  11. package/dist/dashboard/assets/ConsentList-BWOvPOMG.js +1 -0
  12. package/dist/dashboard/assets/ConsentTemplateEditor-BKIEnLmA.js +1 -0
  13. package/dist/dashboard/assets/ConsentTemplateList-DCys2nM3.js +1 -0
  14. package/dist/dashboard/assets/HeadingTagInput-BTjrdzlS.js +1 -0
  15. package/dist/dashboard/assets/HowItWorks-AeyjfQGO.js +1 -0
  16. package/dist/dashboard/assets/PermissionGate-Bhhf7SMh.js +1 -0
  17. package/dist/dashboard/assets/PreviewPane-Dq5K1h2L.js +35 -0
  18. package/dist/dashboard/assets/ProfileEditor-BXtaE0tL.js +2 -0
  19. package/dist/dashboard/assets/ProfileList-CWKSAgu0.js +1 -0
  20. package/dist/dashboard/assets/ProfileVersionHistory-1h8sUgog.js +3 -0
  21. package/dist/dashboard/assets/RecordDetailModal-FaFhzQXi.js +1 -0
  22. package/dist/dashboard/assets/Reports-DWEjQOKh.js +1 -0
  23. package/dist/dashboard/assets/RoleList-BSGoqlzp.js +1 -0
  24. package/dist/dashboard/assets/Select-DjJNwlbY.js +1 -0
  25. package/dist/dashboard/assets/Settings-CVYrlyLm.js +1 -0
  26. package/dist/dashboard/assets/SetupWizard-4NTb5bZj.js +1 -0
  27. package/dist/dashboard/assets/Table-DH_jh8_N.js +1 -0
  28. package/dist/dashboard/assets/TenantList-5AwFtoHf.js +1 -0
  29. package/dist/dashboard/assets/UITemplateEditor-9A55scBe.js +1 -0
  30. package/dist/dashboard/assets/UITemplateList-BS7X4u4F.js +1 -0
  31. package/dist/dashboard/assets/UserList-SPkMyFRy.js +1 -0
  32. package/dist/dashboard/assets/VendorList-C5fPhr6U.js +1 -0
  33. package/dist/dashboard/assets/VisitorList-COP1SyNd.js +1 -0
  34. package/dist/dashboard/assets/check-o5cp1s2J.js +1 -0
  35. package/dist/dashboard/assets/client-ymc9nwWr.js +1 -0
  36. package/dist/dashboard/assets/copy-C01KoUUH.js +1 -0
  37. package/dist/dashboard/assets/createLucideIcon-MdjI3m3u.js +1 -0
  38. package/dist/dashboard/assets/de-DET92Jb5.js +5 -0
  39. package/dist/dashboard/assets/debounce-DlvAAT2V.js +1 -0
  40. package/dist/dashboard/assets/dist-DlhLroYC.js +1 -0
  41. package/dist/dashboard/assets/es-CJNunrnU.js +5 -0
  42. package/dist/dashboard/assets/external-link-DVe00ZBK.js +1 -0
  43. package/dist/dashboard/assets/fr-akfTuf5x.js +5 -0
  44. package/dist/dashboard/assets/history-CHTts_m2.js +1 -0
  45. package/dist/dashboard/assets/index-DW3qOrry.css +2 -0
  46. package/dist/dashboard/assets/index-H_Zm0X-u.js +6 -0
  47. package/dist/dashboard/assets/ja-B0nLWkOd.js +5 -0
  48. package/dist/dashboard/assets/profiles-DmrdGv7t.js +1 -0
  49. package/dist/dashboard/assets/roles-C0dJPFl4.js +1 -0
  50. package/dist/dashboard/assets/search-Bd2RdDRh.js +1 -0
  51. package/dist/dashboard/assets/templates-B2xiivh4.js +1 -0
  52. package/dist/dashboard/assets/templates-CnMoLavN.js +1 -0
  53. package/dist/dashboard/assets/trash-2-BBnkdgm_.js +1 -0
  54. package/dist/dashboard/assets/trash-Dj1jhzHJ.js +1 -0
  55. package/dist/dashboard/assets/useHashQuery-B5ytMIMp.js +1 -0
  56. package/dist/dashboard/assets/users-czK8LRVh.js +1 -0
  57. package/dist/dashboard/assets/x-BRuyBEQP.js +1 -0
  58. package/dist/dashboard/assets/zh-CN-Dxg3G-BL.js +5 -0
  59. package/dist/dashboard/index.html +4 -2
  60. package/dist/dist-Z5RVTVBT.js +2 -0
  61. package/dist/dist-Z5RVTVBT.js.map +1 -0
  62. package/dist/index.cjs +129 -9089
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +963 -254
  65. package/dist/index.d.ts +963 -254
  66. package/dist/index.js +129 -9043
  67. package/dist/index.js.map +1 -1
  68. package/dist/profiles-XTYPWLVC.js +2 -0
  69. package/dist/profiles-XTYPWLVC.js.map +1 -0
  70. package/dist/widget/widget.css +1 -0
  71. package/dist/widget/widget.js +1 -2304
  72. package/package.json +19 -12
  73. package/dist/dashboard/assets/index-BBbhHyc_.js +0 -2637
  74. package/dist/dashboard/assets/index-DYK73WL5.css +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/plugin-engine.ts","../src/tcf/gvl-cache.ts","../src/openapi/spec.ts","../src/utils/crypto.ts","../src/storage/seed-data.ts","../src/storage/sqlite/better-sqlite3.adapter.ts","../src/storage/sqlite/node-sqlite3-wasm.adapter.ts","../src/storage/sqlite/node-sqlite-builtin.adapter.ts","../src/storage/mongodb/mongodb.adapter.ts","../src/storage/mysql/mysql.adapter.ts","../src/storage/postgresql/postgresql.adapter.ts","../src/storage/json/json-file.adapter.ts","../src/repositories/profile.repo.ts","../src/repositories/consent.repo.ts","../src/repositories/visitor.repo.ts","../src/repositories/audit.repo.ts","../src/repositories/user.repo.ts","../src/core/profile-engine.ts","../src/services/profile.service.ts","../src/core/consent-engine.ts","../src/tcf/tc-string.ts","../src/services/consent.service.ts","../src/services/visitor.service.ts","../src/services/user.service.ts","../src/auth/local.auth.ts","../src/utils/http.ts","../src/middleware/error.middleware.ts","../src/routes/public/profile.routes.ts","../src/utils/cookie.ts","../src/middleware/cors.middleware.ts","../src/validators/consent.validator.ts","../src/routes/public/consent.routes.ts","../src/routes/admin/auth.routes.ts","../src/middleware/auth.middleware.ts","../src/auth/oidc.auth.ts","../src/auth/saml.auth.ts","../src/auth/totp.ts","../src/validators/profile.validator.ts","../src/routes/admin/profiles.routes.ts","../src/routes/admin/consents.routes.ts","../src/routes/admin/visitors.routes.ts","../src/routes/admin/users.routes.ts","../src/routes/admin/roles.routes.ts","../src/routes/admin/audit.routes.ts","../src/routes/admin/stats.routes.ts","../src/routes/admin/export.routes.ts","../src/routes/admin/apikeys.routes.ts","../src/routes/admin/tenants.routes.ts","../src/routes/admin/tcf.routes.ts","../src/routes/admin/cookie-templates.routes.ts","../src/routes/admin/ui-templates.routes.ts","../src/middleware/tenant.middleware.ts","../src/middleware/rate-limit.middleware.ts"],"sourcesContent":["import { EventEmitter } from 'node:events'\nimport { readFile } from 'node:fs/promises'\nimport { join, extname, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { PluginEngine } from './core/plugin-engine'\nimport { startGvlRefresh, stopGvlRefresh } from './tcf/gvl-cache'\nimport { OPENAPI_PUBLIC_SPEC, OPENAPI_ADMIN_SPEC } from './openapi/spec'\nimport { BetterSqlite3Adapter } from './storage/sqlite/better-sqlite3.adapter'\nimport { NodeSqlite3WasmAdapter } from './storage/sqlite/node-sqlite3-wasm.adapter'\nimport { NodeSqliteBuiltinAdapter } from './storage/sqlite/node-sqlite-builtin.adapter'\nimport { MongoDBAdapter } from './storage/mongodb/mongodb.adapter'\nimport { MySQLAdapter } from './storage/mysql/mysql.adapter'\nimport { PostgreSQLAdapter } from './storage/postgresql/postgresql.adapter'\nimport { JsonFileAdapter } from './storage/json/json-file.adapter'\nimport { ProfileRepo } from './repositories/profile.repo'\nimport { ConsentRepo } from './repositories/consent.repo'\nimport { VisitorRepo } from './repositories/visitor.repo'\nimport { AuditRepo } from './repositories/audit.repo'\nimport { UserRepo } from './repositories/user.repo'\nimport { ProfileService } from './services/profile.service'\nimport { ConsentService } from './services/consent.service'\nimport { VisitorService } from './services/visitor.service'\nimport { UserService } from './services/user.service'\nimport { LocalAuth } from './auth/local.auth'\nimport { buildProfileRoutes } from './routes/public/profile.routes'\nimport { buildConsentRoutes } from './routes/public/consent.routes'\nimport { buildAdminAuthRoutes } from './routes/admin/auth.routes'\nimport { buildAdminProfileRoutes } from './routes/admin/profiles.routes'\nimport { buildAdminConsentRoutes } from './routes/admin/consents.routes'\nimport { buildAdminVisitorRoutes } from './routes/admin/visitors.routes'\nimport { buildAdminUserRoutes } from './routes/admin/users.routes'\nimport { buildAdminRoleRoutes } from './routes/admin/roles.routes'\nimport { buildAdminAuditRoutes } from './routes/admin/audit.routes'\nimport { buildAdminStatsRoutes } from './routes/admin/stats.routes'\nimport { buildAdminExportRoutes } from './routes/admin/export.routes'\nimport { buildAdminApiKeyRoutes } from './routes/admin/apikeys.routes'\nimport { buildAdminTenantRoutes } from './routes/admin/tenants.routes'\nimport { buildAdminTcfRoutes } from './routes/admin/tcf.routes'\nimport { buildAdminCookieTemplateRoutes } from './routes/admin/cookie-templates.routes'\nimport { buildAdminUITemplateRoutes } from './routes/admin/ui-templates.routes'\nimport { resolveTenant } from './middleware/tenant.middleware'\nimport { RateLimiter } from './middleware/rate-limit.middleware'\nimport { handleCors, withCors, addSecurityHeaders } from './middleware/cors.middleware'\nimport { errorResponse } from './middleware/error.middleware'\nimport { nodeRequestToFetch, fetchResponseToNode, PayloadTooLargeError } from './utils/http'\nimport type { ConsentiServerConfig, StorageAdapter, StorageConfig } from '@consenti/types'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\n// ── Path resolution ────────────────────────────────────────────────────────────\n\nconst DIST_DIR = dirname(fileURLToPath(import.meta.url))\n// new URL pattern is recognised by @vercel/nft (Next.js standalone tracer),\n// causing it to include the dashboard/ directory for any consumer's standalone build.\nexport const dashboardDir: string = fileURLToPath(new URL('./dashboard', import.meta.url))\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n '.ttf': 'font/ttf',\n}\n\nasync function tryServeDashboard(\n pathname: string,\n basePath: string,\n dashboardDir: string,\n res: ServerResponse,\n): Promise<boolean> {\n const rel = pathname.slice(basePath.length) || '/'\n if (rel.startsWith('/api/') || rel.startsWith('/admin/')) return false\n const target = rel === '/' || rel === '' || !extname(rel)\n ? join(dashboardDir, 'index.html')\n : join(dashboardDir, rel)\n // directory traversal guard\n if (!target.startsWith(dashboardDir)) return false\n try {\n const data = await readFile(target)\n const ct = MIME[extname(target).toLowerCase()] ?? 'application/octet-stream'\n res.writeHead(200, { 'Content-Type': ct })\n res.end(data)\n return true\n } catch {\n return false\n }\n}\n\nasync function serveDistFile(filePath: string, contentType: string): Promise<Response> {\n try {\n const data = await readFile(filePath)\n return new Response(data, { headers: { 'Content-Type': contentType, 'Cache-Control': 'public, max-age=3600' } })\n } catch {\n return new Response('Not Found', { status: 404 })\n }\n}\n\n// ── Minimal path router ────────────────────────────────────────────────────────\n\ntype Handler = (req: Request, params: Record<string, string>) => Promise<Response>\n\ninterface RouteEntry {\n method: string\n pattern: RegExp\n paramNames: string[]\n handler: Handler\n rateLimit: boolean\n}\n\nfunction compilePath(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = []\n const regexParts = path.split('/').map(part => {\n if (part.startsWith(':')) {\n paramNames.push(part.slice(1))\n return '([^/]+)'\n }\n return part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n })\n return { pattern: new RegExp(`^${regexParts.join('\\\\/')}$`), paramNames }\n}\n\ninterface RouteGroup {\n prefix: string\n routes: Record<string, Handler>\n rateLimit?: boolean\n}\n\nfunction branded404(basePath: string): Response {\n const docsHref = `${basePath}/api/docs`\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>404 — Consenti</title>\n<style>\n *{box-sizing:border-box;margin:0;padding:0}\n body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0f1117;color:#e2e8f0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem}\n .card{text-align:center;max-width:480px;width:100%}\n .badge{display:inline-flex;align-items:center;gap:.5rem;background:#1e2330;border:1px solid #2d3347;border-radius:999px;padding:.35rem .85rem;font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;color:#7c8db5;margin-bottom:2rem}\n .badge svg{width:14px;height:14px;fill:#4f6ef7}\n h1{font-size:6rem;font-weight:800;line-height:1;background:linear-gradient(135deg,#4f6ef7,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:.5rem}\n h2{font-size:1.25rem;font-weight:600;color:#cbd5e1;margin-bottom:.75rem}\n p{font-size:.9rem;color:#64748b;line-height:1.6;margin-bottom:2rem}\n a{display:inline-flex;align-items:center;gap:.4rem;background:#4f6ef7;color:#fff;text-decoration:none;padding:.6rem 1.25rem;border-radius:.5rem;font-size:.875rem;font-weight:500;transition:background .15s}\n a:hover{background:#3b5ce6}\n a svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}\n</style>\n</head>\n<body>\n<div class=\"card\">\n <div class=\"badge\">\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1a7 7 0 100 14A7 7 0 008 1zm0 3a1 1 0 110 2 1 1 0 010-2zm1 7H7V7h2v4z\"/></svg>\n Consenti\n </div>\n <h1>404</h1>\n <h2>Page not found</h2>\n <p>The admin dashboard is not enabled on this instance.<br>Use the API or enable <code style=\"background:#1e2330;padding:.1rem .35rem;border-radius:.25rem;font-size:.8rem\">dashboard: true</code> in your config.</p>\n <a href=\"${docsHref}\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>\n View API Documentation\n </a>\n</div>\n</body>\n</html>`\n return new Response(html, { status: 404, headers: { 'Content-Type': 'text/html; charset=utf-8' } })\n}\n\nfunction buildFetchRouter(\n basePath: string,\n groups: RouteGroup[],\n rateLimiter: RateLimiter | null,\n): (request: Request) => Promise<Response> {\n const entries: RouteEntry[] = []\n\n for (const group of groups) {\n for (const [key, handler] of Object.entries(group.routes)) {\n const spaceIdx = key.indexOf(' ')\n const method = key.slice(0, spaceIdx).toUpperCase()\n const path = key.slice(spaceIdx + 1)\n const { pattern, paramNames } = compilePath(`${group.prefix}${path}`)\n entries.push({ method, pattern, paramNames, handler, rateLimit: group.rateLimit ?? false })\n }\n }\n\n return async (request: Request): Promise<Response> => {\n const corsResponse = handleCors(request)\n if (corsResponse) return corsResponse\n\n const url = new URL(request.url)\n const pathname = url.pathname\n const method = request.method.toUpperCase()\n\n if (rateLimiter) {\n const matched = entries.find(e => e.rateLimit && pathname.match(e.pattern))\n if (matched) {\n const ip = request.headers.get('x-real-ip') ?? ''\n if (!rateLimiter.checkRequest(ip)) {\n return withCors(errorResponse(429, 'Too Many Requests'), request)\n }\n }\n }\n\n for (const entry of entries) {\n if (entry.method !== method) continue\n const match = pathname.match(entry.pattern)\n if (!match) continue\n const params: Record<string, string> = {}\n entry.paramNames.forEach((name, i) => {\n const val = match[i + 1]\n if (val != null) params[name] = val\n })\n const response = await entry.handler(request, params)\n return addSecurityHeaders(withCors(response, request))\n }\n\n const isUiPath =\n pathname.startsWith(basePath) &&\n !pathname.startsWith(`${basePath}/api/`) &&\n !pathname.startsWith(`${basePath}/admin/`)\n\n return addSecurityHeaders(\n withCors(isUiPath ? branded404(basePath) : errorResponse(404, 'Not Found'), request),\n )\n }\n}\n\n// ── Deep merge ────────────────────────────────────────────────────────────────\n\nfunction isPlainObj(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base }\n for (const key of Object.keys(override)) {\n const ov = override[key]\n const bv = result[key]\n if (ov !== undefined && isPlainObj(ov) && isPlainObj(bv)) {\n result[key] = deepMerge(bv, ov)\n } else if (ov !== undefined) {\n result[key] = ov\n }\n }\n return result\n}\n\n// ── Default config ────────────────────────────────────────────────────────────\n\nconst DEFAULT_CONFIG: ConsentiServerConfig = {\n dashboard: true,\n basePath: process.env['CONSENTI_BASE_PATH'] ?? '/consenti',\n ...(process.env['CONSENTI_MAX_BODY_SIZE'] ? { maxBodySize: Number(process.env['CONSENTI_MAX_BODY_SIZE']) } : {}),\n storage: {\n driver: (process.env['CONSENTI_DB_DRIVER'] as StorageConfig['driver'] | undefined) ?? 'json',\n path: process.env['CONSENTI_DB_PATH'] ?? './consenti-data.json',\n ...(process.env['CONSENTI_DB_URI'] ? { uri: process.env['CONSENTI_DB_URI'] } : {}),\n ...(process.env['CONSENTI_DB_NAME'] ? { dbName: process.env['CONSENTI_DB_NAME'] } : {}),\n ...(process.env['CONSENTI_DB_DATABASE'] ? { database: process.env['CONSENTI_DB_DATABASE'] } : {}),\n ...(process.env['CONSENTI_DB_HOST'] ? { host: process.env['CONSENTI_DB_HOST'] } : {}),\n ...(process.env['CONSENTI_DB_PORT'] ? { port: Number(process.env['CONSENTI_DB_PORT']) } : {}),\n ...(process.env['CONSENTI_DB_USER'] ? { user: process.env['CONSENTI_DB_USER'] } : {}),\n ...(process.env['CONSENTI_DB_PASSWORD'] ? { password: process.env['CONSENTI_DB_PASSWORD'] } : {}),\n },\n auth: {\n mode: 'local',\n adminEmail: process.env['CONSENTI_ADMIN_EMAIL'] ?? 'user@consenti.dev',\n adminPassword: process.env['CONSENTI_ADMIN_PASSWORD'] ?? 'Consenti@123',\n ...(process.env['CONSENTI_ADMIN_JWT_SECRET'] ? { jwtSecret: process.env['CONSENTI_ADMIN_JWT_SECRET'] } : {}),\n },\n rateLimit: {\n windowMs: process.env['CONSENTI_RATE_LIMIT_WINDOW_MS'] ? Number(process.env['CONSENTI_RATE_LIMIT_WINDOW_MS']) : 60_000,\n maxRequests: process.env['CONSENTI_RATE_LIMIT_MAX_REQUESTS'] ? Number(process.env['CONSENTI_RATE_LIMIT_MAX_REQUESTS']) : 60,\n },\n}\n\n// ── Factory ────────────────────────────────────────────────────────────────────\n\nfunction createStorageAdapter(config: NonNullable<ConsentiServerConfig['storage']>): StorageAdapter {\n switch (config.driver) {\n case 'mongodb': return new MongoDBAdapter(config)\n case 'mysql': return new MySQLAdapter(config)\n case 'postgresql': return new PostgreSQLAdapter(config)\n case 'node:sqlite': return new NodeSqliteBuiltinAdapter(config)\n case 'better-sqlite3':\n case 'sqlite': return new BetterSqlite3Adapter(config)\n case 'node-sqlite3-wasm': new NodeSqlite3WasmAdapter(config)\n case 'json':\n default: return new JsonFileAdapter(config)\n }\n}\n\nexport function createConsenti(userConfig: ConsentiServerConfig) {\n const config = deepMerge(\n DEFAULT_CONFIG as unknown as Record<string, unknown>,\n userConfig as unknown as Record<string, unknown>,\n ) as ConsentiServerConfig\n\n if (!userConfig.storage) {\n console.warn('[Consenti] No storage config — using node-sqlite3-wasm (SQLite WASM) at ./consenti-data.db. Switch to a database driver for production.')\n }\n if (!userConfig.auth && !process.env['CONSENTI_ADMIN_PASSWORD']) {\n console.warn('[Consenti] No auth config or CONSENTI_ADMIN_PASSWORD env var — using demo credentials. Set CONSENTI_ADMIN_EMAIL and CONSENTI_ADMIN_PASSWORD before going to production.')\n }\n\n const storageConfig = config.storage!\n const authConfig = config.auth!\n\n const storage = createStorageAdapter(storageConfig)\n const eventBus = new EventEmitter()\n\n const profileRepo = new ProfileRepo(storage)\n const consentRepo = new ConsentRepo(storage)\n const visitorRepo = new VisitorRepo(storage)\n const auditRepo = new AuditRepo(storage)\n const userRepo = new UserRepo(storage)\n\n const pluginEngine = new PluginEngine()\n if (config.plugins?.length) pluginEngine.register(config.plugins)\n\n const profileService = new ProfileService(profileRepo, auditRepo, 'default', storage)\n const visitorService = new VisitorService(visitorRepo)\n const consentService = new ConsentService(\n consentRepo, visitorRepo, profileRepo, auditRepo, 'default', config.compliance,\n pluginEngine, eventBus, config.tcf,\n )\n const userService = new UserService(userRepo, auditRepo)\n\n const adminSecret = authConfig.jwtSecret ?? ''\n const localAuth = new LocalAuth(storage, adminSecret)\n\n const rl = config.rateLimit!\n const rateLimiter = rl.enabled === false\n ? null\n : new RateLimiter(rl.windowMs!, rl.maxRequests!)\n const authLimiter = new RateLimiter(15 * 60_000, 10)\n\n const multiTenantEnabled = config.multiTenant?.enabled === true\n const resolveTenantId = multiTenantEnabled\n ? (req: Request) => resolveTenant(req, storage, 'default')\n : async (_req: Request) => 'default'\n\n const basePath = config.basePath!\n const apiBase = `${basePath}/api/v1`\n const adminBase = `${basePath}/admin`\n const docsPath = `${basePath}/api`\n\n const dashboardEnabled = !!config.dashboard\n const dashboardDir = join(DIST_DIR, 'dashboard')\n\n const swaggerHtml = `<!DOCTYPE html><html><head><title>Consenti API Docs</title>\n<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5.17.14/swagger-ui.css\" crossorigin=\"anonymous\"></head>\n<body><div id=\"swagger-ui\"></div>\n<script src=\"https://unpkg.com/swagger-ui-dist@5.17.14/swagger-ui-bundle.js\" crossorigin=\"anonymous\"></script>\n<script src=\"https://unpkg.com/swagger-ui-dist@5.17.14/swagger-ui-standalone-preset.js\" crossorigin=\"anonymous\"></script>\n<script>SwaggerUIBundle({urls:[{url:'${docsPath}/openapi.json',name:'Public API (/consenti/api)'},{url:'${docsPath}/openapi-admin.json',name:'Admin API (/consenti/admin)'}],'urls.primaryName':'Public API (/consenti/api)',dom_id:'#swagger-ui',presets:[SwaggerUIBundle.presets.apis,SwaggerUIStandalonePreset],layout:'StandaloneLayout'})</script>\n</body></html>`\n\n const robotsTxt = `User-agent: *\\nDisallow: ${basePath}/\\n`\n\n const fetchRouter = buildFetchRouter(\n basePath,\n [\n {\n prefix: docsPath,\n rateLimit: false,\n routes: {\n 'GET /docs': async () => new Response(swaggerHtml, { headers: { 'Content-Type': 'text/html; charset=utf-8' } }),\n 'GET /openapi.json': async () => new Response(JSON.stringify(OPENAPI_PUBLIC_SPEC, null, 2), { headers: { 'Content-Type': 'application/json' } }),\n 'GET /openapi-admin.json': async () => new Response(JSON.stringify(OPENAPI_ADMIN_SPEC, null, 2), { headers: { 'Content-Type': 'application/json' } }),\n },\n },\n {\n prefix: '',\n rateLimit: false,\n routes: {\n 'GET /robots.txt': async () => new Response(robotsTxt, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }),\n },\n },\n {\n prefix: apiBase,\n rateLimit: true,\n routes: {\n ...buildProfileRoutes(profileService),\n ...buildConsentRoutes(consentService, visitorService, profileService, resolveTenantId),\n },\n },\n {\n prefix: adminBase,\n rateLimit: false,\n routes: {\n 'GET /widget.js': async () => serveDistFile(join(DIST_DIR, 'widget', 'widget.js'), 'application/javascript; charset=utf-8'),\n 'GET /widget.css': async () => serveDistFile(join(DIST_DIR, 'widget', 'widget.css'), 'text/css; charset=utf-8'),\n ...buildAdminAuthRoutes(localAuth, storage, authConfig, adminSecret, authLimiter),\n ...buildAdminProfileRoutes(profileService, storage, authConfig, adminSecret),\n ...buildAdminConsentRoutes(storage, authConfig, adminSecret),\n ...buildAdminVisitorRoutes(storage, authConfig, adminSecret),\n ...buildAdminUserRoutes(userService, storage, authConfig, adminSecret),\n ...buildAdminRoleRoutes(storage, authConfig, adminSecret),\n ...buildAdminAuditRoutes(storage, authConfig, adminSecret),\n ...buildAdminStatsRoutes(storage, authConfig, adminSecret),\n ...buildAdminExportRoutes(storage, authConfig, adminSecret),\n ...buildAdminApiKeyRoutes(storage, authConfig, adminSecret),\n ...buildAdminTenantRoutes(storage, authConfig, adminSecret),\n ...buildAdminTcfRoutes(storage, authConfig, adminSecret),\n ...buildAdminCookieTemplateRoutes(storage, authConfig, adminSecret),\n ...buildAdminUITemplateRoutes(storage, authConfig, adminSecret),\n },\n },\n ],\n rateLimiter,\n )\n\n let retentionTimer: ReturnType<typeof setInterval> | null = null\n\n storage\n .connect()\n .then(async () => {\n if (authConfig.mode === 'local' && authConfig.adminEmail && authConfig.adminPassword) {\n await localAuth.bootstrap(authConfig.adminEmail, authConfig.adminPassword)\n }\n await pluginEngine.initialize({ storage, config })\n eventBus.emit('ready')\n\n if (config.tcf?.enabled) {\n startGvlRefresh()\n }\n\n if (config.dataRetention?.purgeAfterDays) {\n const days = config.dataRetention.purgeAfterDays\n const purge = () => storage.purgeExpiredConsents(days)\n .then(n => { if (n > 0) console.warn(`[consenti] Purged ${n} consent records older than ${days} days`) })\n .catch((err: unknown) => console.warn('[consenti] Data retention purge failed:', err))\n void purge()\n retentionTimer = setInterval(() => { void purge() }, 24 * 60 * 60_000)\n }\n })\n .catch((err: unknown) => console.warn('[consenti] Storage connection failed:', err))\n\n async function nodeHandler(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const rawProto = req.headers['x-forwarded-proto']\n const proto = rawProto === 'https' ? 'https' : 'http'\n const host = req.headers['host'] ?? 'localhost'\n const url = new URL(req.url ?? '/', `${proto}://${host}`)\n\n if (dashboardEnabled && url.pathname.startsWith(basePath)) {\n const served = await tryServeDashboard(url.pathname, basePath, dashboardDir, res)\n if (served) return\n }\n\n const request = await nodeRequestToFetch(req, config.maxBodySize, config.trustedProxies)\n const response = await fetchRouter(request)\n\n if (url.pathname === `${basePath}/api/openapi.json`) {\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(OPENAPI_PUBLIC_SPEC, null, 2))\n return\n }\n\n if (url.pathname === `${basePath}/api/openapi-admin.json`) {\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(OPENAPI_ADMIN_SPEC, null, 2))\n return\n }\n\n if (url.pathname === `${basePath}/api/docs`) {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(swaggerHtml)\n return\n }\n\n if (\n response.status === 404 &&\n dashboardEnabled &&\n url.pathname.startsWith(basePath) &&\n !url.pathname.startsWith(basePath + '/api/') &&\n !url.pathname.startsWith(basePath + '/admin/')\n ) {\n try {\n const html = await readFile(join(dashboardDir, 'index.html'))\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(html)\n return\n } catch {\n // dashboard not built — fall through\n }\n }\n\n await fetchResponseToNode(response, res)\n } catch (err) {\n if (!res.headersSent) {\n if (err instanceof PayloadTooLargeError) {\n res.writeHead(413, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Payload Too Large' }))\n } else {\n res.writeHead(500, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Internal Server Error' }))\n }\n }\n }\n }\n\n function router(\n req: IncomingMessage,\n res: ServerResponse,\n next: (err?: unknown) => void,\n ): void {\n nodeHandler(req, res)\n .then(() => { if (!res.writableEnded) next() })\n .catch(next)\n }\n\n function handler(req: IncomingMessage, res: ServerResponse): Promise<void> {\n return nodeHandler(req, res)\n }\n\n const fastifyPlugin = async (\n fastify: {\n all: (\n path: string,\n handler: (req: { raw: IncomingMessage }, reply: { raw: ServerResponse; hijack: () => void }) => Promise<void>,\n ) => void\n },\n _opts: unknown,\n ): Promise<void> => {\n fastify.all('/*', async (request, reply) => {\n await nodeHandler(request.raw, reply.raw)\n reply.hijack()\n })\n }\n Object.defineProperty(fastifyPlugin, Symbol.for('skip-override'), { value: true })\n\n const fetchHandler = async (request: Request): Promise<Response> => {\n if (dashboardEnabled) {\n const url = new URL(request.url)\n const pathname = url.pathname\n if (pathname.startsWith(basePath)) {\n const rel = pathname.slice(basePath.length) || '/'\n if (!rel.startsWith('/api/') && !rel.startsWith('/admin/')) {\n const target = extname(rel) ? join(dashboardDir, rel) : join(dashboardDir, 'index.html')\n if (!target.startsWith(dashboardDir)) {\n return new Response('Forbidden', { status: 403 })\n }\n try {\n const data = await readFile(target)\n const ct = MIME[extname(target).toLowerCase()] ?? 'application/octet-stream'\n return new Response(data, { headers: { 'Content-Type': ct } })\n } catch {\n try {\n const html = await readFile(join(dashboardDir, 'index.html'))\n return new Response(html, { headers: { 'Content-Type': 'text/html; charset=utf-8' } })\n } catch {\n return new Response(\n 'Dashboard not built. Run: npm run build --workspace=apps/api',\n { status: 404, headers: { 'Content-Type': 'text/plain' } },\n )\n }\n }\n }\n }\n }\n return fetchRouter(request)\n }\n\n const honoApp = { fetch: fetchHandler }\n\n const services = {\n profile: profileService,\n consent: consentService,\n visitor: visitorService,\n user: userService,\n }\n\n return {\n router,\n handler,\n fastifyPlugin,\n honoApp,\n services,\n storage: storage as StorageAdapter,\n eventBus,\n destroy: async () => {\n if (retentionTimer) clearInterval(retentionTimer)\n if (config.tcf?.enabled) stopGvlRefresh()\n await pluginEngine.destroy()\n },\n }\n}\n\nexport { SQLiteAdapter } from './storage/sqlite/sqlite.adapter'\nexport { MongoDBAdapter } from './storage/mongodb/mongodb.adapter'\nexport { MySQLAdapter } from './storage/mysql/mysql.adapter'\nexport { PostgreSQLAdapter } from './storage/postgresql/postgresql.adapter'\n\nexport type {\n ConsentiServerConfig,\n StorageAdapter,\n Profile,\n ConsentDbRecord,\n Visitor,\n AdminUser,\n Role,\n Permission,\n AuditLog,\n ConsentVerifyResult,\n SafeAdminUser,\n} from '@consenti/types'\n","import type {\n ConsentiServerPlugin,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentDbRecord,\n Profile,\n CreateUserInput,\n AdminUser,\n PluginContext,\n} from '@consenti/types'\n\nexport class PluginEngine {\n private plugins: ConsentiServerPlugin[] = []\n\n register(plugins: ConsentiServerPlugin[]): void {\n this.plugins = plugins\n }\n\n async initialize(context: PluginContext): Promise<void> {\n for (const plugin of this.plugins) {\n try { await plugin.initialize?.(context) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" initialize failed:`, err) }\n }\n }\n\n async destroy(): Promise<void> {\n for (const plugin of this.plugins) {\n try { await plugin.destroy?.() }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" destroy failed:`, err) }\n }\n }\n\n async runBeforeConsentSave(data: CreateConsentInput): Promise<CreateConsentInput> {\n let result = data\n for (const plugin of this.plugins) {\n if (!plugin.beforeConsentSave) continue\n try { result = await plugin.beforeConsentSave(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" beforeConsentSave failed:`, err) }\n }\n return result\n }\n\n async runAfterConsentSave(record: ConsentDbRecord): Promise<void> {\n for (const plugin of this.plugins) {\n if (!plugin.afterConsentSave) continue\n try { await plugin.afterConsentSave(record) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterConsentSave failed:`, err) }\n }\n }\n\n async runBeforeConsentUpdate(data: UpdateConsentInput): Promise<UpdateConsentInput> {\n let result = data\n for (const plugin of this.plugins) {\n if (!plugin.beforeConsentUpdate) continue\n try { result = await plugin.beforeConsentUpdate(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" beforeConsentUpdate failed:`, err) }\n }\n return result\n }\n\n async runAfterConsentUpdate(record: ConsentDbRecord): Promise<void> {\n for (const plugin of this.plugins) {\n if (!plugin.afterConsentUpdate) continue\n try { await plugin.afterConsentUpdate(record) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterConsentUpdate failed:`, err) }\n }\n }\n\n async runAfterProfileFetch(profile: Profile): Promise<Profile> {\n let result = profile\n for (const plugin of this.plugins) {\n if (!plugin.afterProfileFetch) continue\n try { result = await plugin.afterProfileFetch(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterProfileFetch failed:`, err) }\n }\n return result\n }\n\n async runBeforeUserCreate(data: CreateUserInput): Promise<CreateUserInput> {\n let result = data\n for (const plugin of this.plugins) {\n if (!plugin.beforeUserCreate) continue\n try { result = await plugin.beforeUserCreate(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" beforeUserCreate failed:`, err) }\n }\n return result\n }\n\n async runAfterUserCreate(user: AdminUser): Promise<void> {\n for (const plugin of this.plugins) {\n if (!plugin.afterUserCreate) continue\n try { await plugin.afterUserCreate(user) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterUserCreate failed:`, err) }\n }\n }\n}\n","const GVL_URL = 'https://vendor-list.consensu.org/v3/vendor-list.json'\nconst REFRESH_MS = 7 * 24 * 60 * 60 * 1000\n\nexport interface GvlPurpose {\n id: number\n name: string\n description: string\n}\n\nexport interface GvlVendor {\n id: number\n name: string\n purposes?: number[]\n legIntPurposes?: number[]\n flexiblePurposes?: number[]\n specialPurposes?: number[]\n features?: number[]\n specialFeatures?: number[]\n}\n\nexport interface GvlData {\n gvlSpecificationVersion: number\n vendorListVersion: number\n vendors: Record<string, GvlVendor>\n purposes: Record<string, GvlPurpose>\n}\n\nlet cachedGvl: GvlData | null = null\nlet lastFetched = 0\nlet refreshTimer: ReturnType<typeof setInterval> | null = null\n\nfunction isGvlData(d: unknown): d is GvlData {\n return typeof d === 'object' && d !== null &&\n 'vendors' in d && 'purposes' in d &&\n typeof (d as GvlData).vendors === 'object' &&\n typeof (d as GvlData).purposes === 'object'\n}\n\nexport async function getGvl(): Promise<GvlData | null> {\n if (cachedGvl && Date.now() - lastFetched < REFRESH_MS) return cachedGvl\n try {\n const res = await fetch(GVL_URL, { signal: AbortSignal.timeout(15_000) })\n if (!res.ok) return cachedGvl\n const data = await res.json()\n if (!isGvlData(data)) return cachedGvl\n cachedGvl = data\n lastFetched = Date.now()\n return cachedGvl\n } catch {\n return cachedGvl\n }\n}\n\nexport function startGvlRefresh(): void {\n if (refreshTimer) return\n void getGvl()\n refreshTimer = setInterval(() => { void getGvl() }, REFRESH_MS)\n}\n\nexport function stopGvlRefresh(): void {\n if (!refreshTimer) return\n clearInterval(refreshTimer)\n refreshTimer = null\n}\n","const SHARED_SCHEMAS = {\n ConsentRecord: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n visitorId: { type: 'string' },\n profileId: { type: 'string' },\n profileVersion: { type: 'integer' },\n locale: { type: 'string' },\n consentJson: { type: 'object', additionalProperties: { type: 'string', enum: ['granted', 'denied', 'objected'] } },\n gpcDetected: { type: 'boolean' },\n source: { type: 'string', enum: ['banner', 'api', 'import'] },\n ageVerified: { type: 'boolean' },\n tcfString: { type: 'string' },\n createdAt: { type: 'string', format: 'date-time' },\n updatedAt: { type: 'string', format: 'date-time' },\n },\n },\n CreateConsentInput: {\n type: 'object',\n required: ['visitorId', 'profileId', 'consentJson'],\n properties: {\n visitorId: { type: 'string' },\n profileId: { type: 'string' },\n consentJson: { type: 'object', additionalProperties: { type: 'string' } },\n gpcDetected: { type: 'boolean', default: false },\n source: { type: 'string', enum: ['banner', 'api', 'import'], default: 'banner' },\n ageVerified: { type: 'boolean' },\n parentalConsentToken: { type: 'string' },\n tcfString: { type: 'string' },\n },\n },\n}\n\nexport const OPENAPI_PUBLIC_SPEC = {\n openapi: '3.1.0',\n info: {\n title: 'Consenti Public API',\n version: '0.1.0',\n description: 'Visitor-facing endpoints for reading consent profiles and submitting/verifying consent. Rate-limited; no authentication required.',\n license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0' },\n },\n servers: [\n { url: '/consenti/api', description: 'Consenti Public API' },\n ],\n paths: {\n '/v1/profiles/{id}': {\n get: {\n tags: ['Profile'],\n summary: 'Get profile in its default locale',\n description: 'Returns the resolved profile with banner and modal content for the profile\\'s default locale. Includes `currentLocale`, `locales`, `cookies`, `mainBanner`, `preferenceModal`, and optionally `gpcBanner`.',\n parameters: [\n { name: 'id', in: 'path', required: true, schema: { type: 'string' } },\n ],\n responses: {\n '200': { description: 'Resolved profile for the default locale' },\n '404': { description: 'Profile not found' },\n },\n },\n },\n '/v1/profiles/{id}/{locale}': {\n get: {\n tags: ['Profile'],\n summary: 'Get profile resolved for a specific locale',\n description: 'Same response shape as `GET /v1/profiles/{id}` but resolved for the requested locale, falling back to the default locale for any missing translations.',\n parameters: [\n { name: 'id', in: 'path', required: true, schema: { type: 'string' } },\n { name: 'locale', in: 'path', required: true, schema: { type: 'string', example: 'fr' } },\n ],\n responses: {\n '200': { description: 'Resolved profile for the requested locale' },\n '404': { description: 'Profile not found' },\n },\n },\n },\n '/v1/consent': {\n post: {\n tags: ['Consent'],\n summary: 'Submit consent',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/CreateConsentInput' },\n },\n },\n },\n responses: {\n '200': { description: 'Consent created or updated', content: { 'application/json': { schema: { $ref: '#/components/schemas/ConsentRecord' } } } },\n '400': { description: 'Validation error' },\n },\n },\n },\n '/v1/consent/{visitorId}': {\n get: {\n tags: ['Consent'],\n summary: 'Get consent for visitor',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: {\n '200': { description: 'Consent record', content: { 'application/json': { schema: { $ref: '#/components/schemas/ConsentRecord' } } } },\n '404': { description: 'Not found' },\n },\n },\n put: {\n tags: ['Consent'],\n summary: 'Update consent',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Updated consent' } },\n },\n delete: {\n tags: ['Consent'],\n summary: 'GDPR erasure — delete all consent data for visitor',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Erased successfully' } },\n },\n },\n '/v1/consent/{visitorId}/verify': {\n get: {\n tags: ['Consent'],\n summary: 'Verify consent validity',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Verification result' } },\n },\n },\n },\n components: {\n schemas: SHARED_SCHEMAS,\n },\n}\n\nexport const OPENAPI_ADMIN_SPEC = {\n openapi: '3.1.0',\n info: {\n title: 'Consenti Admin API',\n version: '0.1.0',\n description: 'Authenticated endpoints for managing profiles, consent records, users, analytics, and exports. All routes (except `/auth/login`) require `Authorization: Bearer <JWT>`. Obtain a token via `POST /auth/login`.',\n license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0' },\n },\n servers: [\n { url: '/consenti/admin', description: 'Consenti Admin API' },\n ],\n paths: {\n // ── Auth ──────────────────────────────────────────────────────────────\n '/auth/login': {\n post: {\n tags: ['Auth'],\n summary: 'Login with email + password',\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { email: { type: 'string' }, password: { type: 'string' } } } } } },\n responses: { '200': { description: 'JWT token' } },\n },\n },\n '/auth/me': {\n get: {\n tags: ['Auth'],\n summary: 'Get current user',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Current admin user info' } },\n },\n },\n '/auth/logout': {\n post: {\n tags: ['Auth'],\n summary: 'Invalidate session',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Logged out' } },\n },\n },\n '/auth/oidc/authorize': {\n get: {\n tags: ['Auth'],\n summary: 'Start OIDC authorization (PKCE)',\n responses: { '302': { description: 'Redirect to OIDC provider' } },\n },\n },\n '/auth/oidc/callback': {\n get: {\n tags: ['Auth'],\n summary: 'OIDC callback — exchange code for token',\n responses: { '200': { description: 'JWT token' } },\n },\n },\n '/auth/saml/metadata': {\n get: {\n tags: ['Auth'],\n summary: 'SAML SP metadata XML',\n responses: { '200': { description: 'SAML metadata XML', content: { 'application/xml': {} } } },\n },\n },\n '/auth/saml/acs': {\n post: {\n tags: ['Auth'],\n summary: 'SAML Assertion Consumer Service',\n responses: { '200': { description: 'JWT token' } },\n },\n },\n '/auth/totp/setup': {\n post: {\n tags: ['Auth'],\n summary: 'Setup TOTP MFA',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'TOTP secret and QR URL' } },\n },\n },\n '/auth/totp/verify': {\n post: {\n tags: ['Auth'],\n summary: 'Verify and enable TOTP',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'TOTP enabled' } },\n },\n },\n // ── Profiles ──────────────────────────────────────────────────────────\n '/profiles': {\n get: {\n tags: ['Profiles'],\n summary: 'List profiles',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Array of profiles' } },\n },\n post: {\n tags: ['Profiles'],\n summary: 'Create profile',\n security: [{ BearerAuth: [] }],\n responses: { '201': { description: 'Created profile' } },\n },\n },\n // ── Consents ──────────────────────────────────────────────────────────\n '/consents': {\n get: {\n tags: ['Consents'],\n summary: 'List consent records',\n security: [{ BearerAuth: [] }],\n parameters: [\n { name: 'page', in: 'query', schema: { type: 'integer' } },\n { name: 'profileId', in: 'query', schema: { type: 'string' } },\n { name: 'from', in: 'query', schema: { type: 'string', format: 'date' } },\n { name: 'to', in: 'query', schema: { type: 'string', format: 'date' } },\n ],\n responses: { '200': { description: 'Paginated consent records' } },\n },\n },\n // ── Stats ─────────────────────────────────────────────────────────────\n '/stats/overview': {\n get: {\n tags: ['Stats'],\n summary: 'Overview statistics',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Totals and acceptance rates' } },\n },\n },\n '/stats/timeline': {\n get: {\n tags: ['Stats'],\n summary: 'Daily consent timeline',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'days', in: 'query', schema: { type: 'integer', default: 30 } }],\n responses: { '200': { description: 'Array of date+count' } },\n },\n },\n '/stats/countries': {\n get: {\n tags: ['Stats'],\n summary: 'Consent by country',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Country counts' } },\n },\n },\n '/stats/gpc': {\n get: {\n tags: ['Stats'],\n summary: 'GPC detection stats',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'GPC rate and counts' } },\n },\n },\n // ── Export ────────────────────────────────────────────────────────────\n '/export/consents': {\n get: {\n tags: ['Export'],\n summary: 'Export consent records',\n security: [{ BearerAuth: [] }],\n parameters: [\n { name: 'format', in: 'query', schema: { type: 'string', enum: ['csv', 'json'] } },\n { name: 'from', in: 'query', schema: { type: 'string', format: 'date' } },\n { name: 'to', in: 'query', schema: { type: 'string', format: 'date' } },\n ],\n responses: { '200': { description: 'CSV or JSON download' } },\n },\n },\n // ── Tenants ───────────────────────────────────────────────────────────\n '/tenants': {\n get: {\n tags: ['Sites'],\n summary: 'List all sites/tenants',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Array of tenants' } },\n },\n post: {\n tags: ['Sites'],\n summary: 'Create a site/tenant',\n security: [{ BearerAuth: [] }],\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { name: { type: 'string' }, slug: { type: 'string' } }, required: ['name', 'slug'] } } } },\n responses: { '201': { description: 'Created tenant' } },\n },\n },\n '/tenants/{id}': {\n put: {\n tags: ['Sites'],\n summary: 'Update a site/tenant',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Updated tenant' } },\n },\n delete: {\n tags: ['Sites'],\n summary: 'Delete a site/tenant',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Deleted' } },\n },\n },\n },\n components: {\n securitySchemes: {\n BearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },\n },\n schemas: SHARED_SCHEMAS,\n },\n}\n","import { createHash, createHmac, randomUUID, scryptSync, randomBytes, timingSafeEqual } from 'node:crypto'\n\nexport { randomUUID }\n\nexport function randomProfileId(): string {\n return `prof_${randomBytes(20).toString('hex')}`\n}\n\nexport function hashIp(ip: string): string {\n return createHash('sha256').update(ip).digest('hex')\n}\n\nexport function hashUserAgent(ua: string): string {\n return createHash('sha256').update(ua).digest('hex')\n}\n\nexport function signHmac(payload: string, secret: string): string {\n return createHmac('sha256', secret).update(payload).digest('hex')\n}\n\nexport function verifyHmac(payload: string, signature: string, secret: string): boolean {\n const expected = Buffer.from(signHmac(payload, secret), 'hex')\n const actual = Buffer.from(signature, 'hex')\n if (expected.length !== actual.length) return false\n return timingSafeEqual(expected, actual)\n}\n\nexport function hashPassword(password: string): string {\n const salt = randomBytes(16).toString('hex')\n const hash = scryptSync(password, salt, 64).toString('hex')\n return `${salt}:${hash}`\n}\n\nexport function verifyPassword(password: string, stored: string): boolean {\n const [salt, hash] = stored.split(':')\n if (!salt || !hash) return false\n const derived = scryptSync(password, salt, 64).toString('hex')\n return timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(derived, 'hex'))\n}\n\nexport function signJwt(payload: Record<string, unknown>, secret: string, expiresInSeconds = 3600): string {\n const now = Math.floor(Date.now() / 1000)\n const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).replace(/=/g, '')\n const body = btoa(JSON.stringify({ ...payload, iat: now, exp: now + expiresInSeconds })).replace(/=/g, '')\n const sig = signHmac(`${header}.${body}`, secret)\n .match(/.{2}/g)!\n .map(h => String.fromCharCode(parseInt(h, 16)))\n .join('')\n const sigEncoded = btoa(sig).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n return `${header}.${body}.${sigEncoded}`\n}\n\nexport function verifyJwt(token: string, secret: string): Record<string, unknown> | null {\n const parts = token.split('.')\n if (parts.length !== 3) return null\n const [header, body, sigEncoded] = parts\n if (!header || !body || !sigEncoded) return null\n const expected = signHmac(`${header}.${body}`, secret)\n const sigBytes = atob(sigEncoded.replace(/-/g, '+').replace(/_/g, '/'))\n const actualHex = Array.from(sigBytes).map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join('')\n const expectedBuf = Buffer.from(expected, 'hex')\n const actualBuf = Buffer.from(actualHex, 'hex')\n if (expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf)) return null\n try {\n const payload = JSON.parse(atob(body)) as Record<string, unknown>\n const exp = payload['exp']\n if (typeof exp === 'number' && Date.now() / 1000 > exp) return null\n return payload\n } catch {\n return null\n }\n}\n","// Single source of truth for schema definitions, seed data, and SQL generation.\n// Document DBs (JSON, MongoDB) use the JS objects directly.\n// SQL DBs (SQLite, PostgreSQL, MySQL) import the generated SQL exports.\n\n// ── Schema definition types ───────────────────────────────────────────────────\n\n/** Abstract column types — each SQL dialect maps these to its native type. */\nexport type ColType =\n | 'pk' // String primary key\n | 'text' // Short-medium text (TEXT in SQLite/PG; VARCHAR(255) in MySQL)\n | 'text_sm' // Short code ≤20 (TEXT in SQLite/PG; VARCHAR(20) in MySQL)\n | 'text_md' // Medium text ≤100 (TEXT in SQLite/PG; VARCHAR(100) in MySQL)\n | 'text_geo' // Geo code ≤10 (TEXT in SQLite/PG; VARCHAR(10) in MySQL)\n | 'text_hash' // Hash string ≤64 (TEXT in SQLite/PG; VARCHAR(64) in MySQL)\n | 'longtext' // Long text (TEXT in SQLite/PG; LONGTEXT in MySQL)\n | 'json' // JSON blob (TEXT in SQLite; JSONB in PG; LONGTEXT in MySQL)\n | 'int' // Integer\n | 'bool' // Boolean (INTEGER 0/1 in SQLite; BOOLEAN in PG; TINYINT(1) in MySQL)\n | 'ts' // Timestamp with current-time default\n\nexport interface ColDef {\n n: string // column name\n t: ColType\n null?: true // nullable; omit = NOT NULL\n def?: string | number | boolean // '$now'→now(), '$arr'→'[]', '$obj'→'{}'\n unique?: true\n ref?: string // REFERENCES <ref>(id)\n onDel?: 'CASCADE'\n mysqlUpd?: true // MySQL: ON UPDATE CURRENT_TIMESTAMP\n}\n\nexport interface TableDef {\n name: string\n cols: ColDef[]\n pk?: string[] // composite primary key\n uq?: { cols: string[] }[] // composite unique constraints\n}\n\n// ── Table definitions ─────────────────────────────────────────────────────────\n\nconst tenants: TableDef = {\n name: 'tenants',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'name', t: 'text' },\n { n: 'slug', t: 'text', unique: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\nconst profiles: TableDef = {\n name: 'profiles',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text' },\n { n: 'default_locale', t: 'text_sm', def: 'en' },\n { n: 'version', t: 'int', def: 1 },\n { n: 'profile_json', t: 'json' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\n// SQLite initial schema — without enterprise columns (added via ALTER TABLE in v3 migration)\nconst consentRecordsBase: TableDef = {\n name: 'consent_records',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'visitor_id', t: 'text' },\n { n: 'profile_id', t: 'text', ref: 'profiles' },\n { n: 'profile_version', t: 'int' },\n { n: 'locale', t: 'text_sm' },\n { n: 'consent_json', t: 'json' },\n { n: 'gpc_detected', t: 'bool', def: false },\n { n: 'source', t: 'text_sm' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n uq: [{ cols: ['visitor_id', 'profile_id'] }],\n}\n\n// Full schema — includes enterprise columns; used for PostgreSQL/MySQL\nconst consentRecordsFull: TableDef = {\n name: 'consent_records',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'visitor_id', t: 'text' },\n { n: 'profile_id', t: 'text', ref: 'profiles' },\n { n: 'profile_version', t: 'int' },\n { n: 'locale', t: 'text_sm' },\n { n: 'consent_json', t: 'json' },\n { n: 'gpc_detected', t: 'bool', def: false },\n { n: 'source', t: 'text_sm' },\n { n: 'age_verified', t: 'bool', def: false },\n { n: 'parental_consent_token', t: 'longtext', null: true },\n { n: 'tcf_string', t: 'longtext', null: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n uq: [{ cols: ['visitor_id', 'profile_id'] }],\n}\n\nconst consentHistory: TableDef = {\n name: 'consent_history',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'consent_record_id', t: 'text', ref: 'consent_records' },\n { n: 'visitor_id', t: 'text' },\n { n: 'old_json', t: 'json', null: true },\n { n: 'new_json', t: 'json' },\n { n: 'action', t: 'text_sm' },\n { n: 'created_at', t: 'ts', def: '$now' },\n ],\n}\n\nconst visitors: TableDef = {\n name: 'visitors',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'visitor_id', t: 'text', unique: true },\n { n: 'country', t: 'text_geo', null: true },\n { n: 'region', t: 'text_md', null: true },\n { n: 'city', t: 'text_md', null: true },\n { n: 'ip_hash', t: 'text_hash', null: true },\n { n: 'user_agent_hash', t: 'text_hash', null: true },\n { n: 'first_seen', t: 'ts', def: '$now' },\n { n: 'last_seen', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\n// SQLite initial schema — without TOTP columns (added via ALTER TABLE in v3 migration)\nconst usersBase: TableDef = {\n name: 'users',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text' },\n { n: 'email', t: 'text', unique: true },\n { n: 'password_hash', t: 'text' },\n { n: 'is_active', t: 'bool', def: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\n// Full schema — includes TOTP columns; used for PostgreSQL/MySQL\nconst usersFull: TableDef = {\n name: 'users',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text' },\n { n: 'email', t: 'text', unique: true },\n { n: 'password_hash', t: 'text' },\n { n: 'is_active', t: 'bool', def: true },\n { n: 'totp_secret', t: 'longtext', null: true },\n { n: 'totp_enabled', t: 'bool', def: false },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\nconst roles: TableDef = {\n name: 'roles',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text_md' },\n { n: 'description', t: 'text', null: true },\n ],\n}\n\nconst permissions: TableDef = {\n name: 'permissions',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'name', t: 'text_md', unique: true },\n { n: 'description', t: 'text', null: true },\n ],\n}\n\n// SQLite v1 — no tenant_id column (added via ALTER TABLE in v4 migration)\nconst userRolesBase: TableDef = {\n name: 'user_roles',\n cols: [\n { n: 'user_id', t: 'text', ref: 'users' },\n { n: 'role_id', t: 'text', ref: 'roles' },\n ],\n pk: ['user_id', 'role_id'],\n}\n\n// Full schema with tenant_id — used for PostgreSQL/MySQL\nconst userRolesFull: TableDef = {\n name: 'user_roles',\n cols: [\n { n: 'user_id', t: 'text', ref: 'users' },\n { n: 'role_id', t: 'text', ref: 'roles' },\n { n: 'tenant_id', t: 'text', def: 'default' },\n ],\n pk: ['user_id', 'role_id', 'tenant_id'],\n}\n\nconst rolePermissions: TableDef = {\n name: 'role_permissions',\n cols: [\n { n: 'role_id', t: 'text', ref: 'roles' },\n { n: 'permission_id', t: 'text', ref: 'permissions' },\n ],\n pk: ['role_id', 'permission_id'],\n}\n\nconst auditLogs: TableDef = {\n name: 'audit_logs',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'user_id', t: 'text', null: true },\n { n: 'action', t: 'text_md' },\n { n: 'resource_type', t: 'text_md' },\n { n: 'resource_id', t: 'text', null: true },\n { n: 'old_data', t: 'json', null: true },\n { n: 'new_data', t: 'json', null: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n ],\n}\n\nconst apiKeys: TableDef = {\n name: 'api_keys',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'key_hash', t: 'text_hash', unique: true },\n { n: 'name', t: 'text' },\n { n: 'is_active', t: 'bool', def: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n ],\n}\n\nconst cookieTemplates: TableDef = {\n name: 'cookie_templates',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'name', t: 'text' },\n { n: 'cookies_json', t: 'json', def: '$arr' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\nconst uiTemplates: TableDef = {\n name: 'ui_templates',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'name', t: 'text' },\n { n: 'settings_json', t: 'json', def: '$obj' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\n// ── Exported table definition groups ─────────────────────────────────────────\n\n/** Core tables for SQLite initial (v1) migration — no enterprise columns, no tenant_id in user_roles. */\nexport const CORE_TABLES_SQLITE: readonly TableDef[] = [\n tenants, profiles, consentRecordsBase, consentHistory, visitors,\n usersBase, roles, permissions, userRolesBase, rolePermissions, auditLogs,\n]\n\n/** Core tables with all current columns — for PostgreSQL/MySQL full initial schema. */\nexport const CORE_TABLES_FULL: readonly TableDef[] = [\n tenants, profiles, consentRecordsFull, consentHistory, visitors,\n usersFull, roles, permissions, userRolesFull, rolePermissions, auditLogs,\n]\n\n/** api_keys table — SQLite v2 migration. */\nexport const API_KEY_TABLES: readonly TableDef[] = [apiKeys]\n\n/** Template tables — SQLite v5 migration. */\nexport const TEMPLATE_TABLES: readonly TableDef[] = [cookieTemplates, uiTemplates]\n\n// ── SQL generators ────────────────────────────────────────────────────────────\n\nfunction sqliteDef(def: string | number | boolean): string {\n if (def === '$now') return `DEFAULT (datetime('now'))`\n if (def === '$arr') return `DEFAULT '[]'`\n if (def === '$obj') return `DEFAULT '{}'`\n if (def === true) return 'DEFAULT 1'\n if (def === false) return 'DEFAULT 0'\n if (typeof def === 'string') return `DEFAULT '${def}'`\n return `DEFAULT ${def}`\n}\n\nfunction buildColSQLite(c: ColDef): string {\n if (c.t === 'pk') return `${c.n} TEXT PRIMARY KEY`\n const type = (c.t === 'int' || c.t === 'bool') ? 'INTEGER' : 'TEXT'\n const parts = [`${c.n} ${type}`]\n if (!c.null) parts.push('NOT NULL')\n if (c.def !== undefined) parts.push(sqliteDef(c.def))\n if (c.unique) parts.push('UNIQUE')\n if (c.ref) { parts.push(`REFERENCES ${c.ref}(id)`); if (c.onDel) parts.push(`ON DELETE ${c.onDel}`) }\n return parts.join(' ')\n}\n\nfunction buildTableSQLite(t: TableDef): string {\n const lines = t.cols.map(c => ` ${buildColSQLite(c)}`)\n if (t.pk) lines.push(` PRIMARY KEY (${t.pk.join(', ')})`)\n if (t.uq) for (const u of t.uq) lines.push(` UNIQUE(${u.cols.join(', ')})`)\n return `CREATE TABLE IF NOT EXISTS ${t.name} (\\n${lines.join(',\\n')}\\n);`\n}\n\nfunction pgDef(def: string | number | boolean): string {\n if (def === '$now') return 'DEFAULT NOW()'\n if (def === '$arr') return `DEFAULT '[]'`\n if (def === '$obj') return `DEFAULT '{}'`\n if (def === true) return 'DEFAULT TRUE'\n if (def === false) return 'DEFAULT FALSE'\n if (typeof def === 'string') return `DEFAULT '${def}'`\n return `DEFAULT ${def}`\n}\n\nfunction buildColPg(c: ColDef): string {\n if (c.t === 'pk') return `${c.n} TEXT PRIMARY KEY`\n const type = c.t === 'json' ? 'JSONB' : c.t === 'int' ? 'INTEGER' : c.t === 'bool' ? 'BOOLEAN' : c.t === 'ts' ? 'TIMESTAMPTZ' : 'TEXT'\n const parts = [`${c.n} ${type}`]\n if (!c.null) parts.push('NOT NULL')\n if (c.def !== undefined) parts.push(pgDef(c.def))\n if (c.unique) parts.push('UNIQUE')\n if (c.ref) { parts.push(`REFERENCES ${c.ref}(id)`); if (c.onDel) parts.push(`ON DELETE ${c.onDel}`) }\n return parts.join(' ')\n}\n\nfunction buildTablePg(t: TableDef): string {\n const lines = t.cols.map(c => ` ${buildColPg(c)}`)\n if (t.pk) lines.push(` PRIMARY KEY (${t.pk.join(', ')})`)\n if (t.uq) for (const u of t.uq) lines.push(` UNIQUE (${u.cols.join(', ')})`)\n return `CREATE TABLE IF NOT EXISTS ${t.name} (\\n${lines.join(',\\n')}\\n);`\n}\n\nconst MYSQL_TEXT: Partial<Record<ColType, string>> = {\n text: 'VARCHAR(255)', text_sm: 'VARCHAR(20)', text_md: 'VARCHAR(100)',\n text_geo: 'VARCHAR(10)', text_hash: 'VARCHAR(64)', longtext: 'LONGTEXT', json: 'LONGTEXT',\n}\n\nfunction mysqlDef(def: string | number | boolean): string {\n if (def === '$now') return 'DEFAULT CURRENT_TIMESTAMP'\n if (def === '$arr') return `DEFAULT '[]'`\n if (def === '$obj') return `DEFAULT '{}'`\n if (def === true) return 'DEFAULT 1'\n if (def === false) return 'DEFAULT 0'\n if (typeof def === 'string') return `DEFAULT '${def}'`\n return `DEFAULT ${def}`\n}\n\nfunction buildColMySQL(c: ColDef): string {\n if (c.t === 'pk') return `${c.n} VARCHAR(36) PRIMARY KEY`\n const type = c.t === 'int' ? 'INT' : c.t === 'bool' ? 'TINYINT(1)' : c.t === 'ts' ? 'DATETIME' : (MYSQL_TEXT[c.t] ?? 'TEXT')\n const parts = [`${c.n} ${type}`]\n if (!c.null) parts.push('NOT NULL')\n if (c.def !== undefined) parts.push(mysqlDef(c.def))\n if (c.mysqlUpd) parts.push('ON UPDATE CURRENT_TIMESTAMP')\n if (c.unique) parts.push('UNIQUE')\n if (c.ref) { parts.push(`REFERENCES ${c.ref}(id)`); if (c.onDel) parts.push(`ON DELETE ${c.onDel}`) }\n return parts.join(' ')\n}\n\nfunction buildTableMySQL(t: TableDef): string {\n const lines = t.cols.map(c => ` ${buildColMySQL(c)}`)\n if (t.pk) lines.push(` PRIMARY KEY (${t.pk.join(', ')})`)\n if (t.uq) for (const u of t.uq) lines.push(` UNIQUE KEY uq_${u.cols.join('_')} (${u.cols.join(', ')})`)\n return `CREATE TABLE IF NOT EXISTS ${t.name} (\\n${lines.join(',\\n')}\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`\n}\n\nfunction toSQLite(tables: readonly TableDef[]): string { return tables.map(buildTableSQLite).join('\\n') }\nfunction toPostgres(tables: readonly TableDef[]): string { return tables.map(buildTablePg).join('\\n') }\nfunction toMySQL(tables: readonly TableDef[]): string { return tables.map(buildTableMySQL).join('\\n') }\n\n// ── Schema SQL exports ────────────────────────────────────────────────────────\n// SQLite: separate blocks matching its versioned migration steps.\n// PostgreSQL/MySQL: one block containing the full current schema.\n\n/** SQLite v1 migration: core tables. */\nexport const SCHEMA_SQL_SQLITE = toSQLite(CORE_TABLES_SQLITE)\n\n/** SQLite v2 migration: api_keys table. */\nexport const SCHEMA_SQL_SQLITE_API_KEYS = toSQLite(API_KEY_TABLES)\n\n/** SQLite v3 migration: enterprise columns added via ALTER TABLE (not generatable from TableDef). */\nexport const SCHEMA_SQL_SQLITE_ENTERPRISE = `\nALTER TABLE consent_records ADD COLUMN age_verified INTEGER NOT NULL DEFAULT 0;\nALTER TABLE consent_records ADD COLUMN parental_consent_token TEXT;\nALTER TABLE consent_records ADD COLUMN tcf_string TEXT;\nALTER TABLE users ADD COLUMN totp_secret TEXT;\nALTER TABLE users ADD COLUMN totp_enabled INTEGER NOT NULL DEFAULT 0;\n`\n\n/** SQLite v5 migration: template tables. */\nexport const SCHEMA_SQL_SQLITE_TEMPLATES = toSQLite(TEMPLATE_TABLES)\n\nconst ALL_TABLES_FULL: readonly TableDef[] = [...CORE_TABLES_FULL, ...API_KEY_TABLES, ...TEMPLATE_TABLES]\n\n/** PostgreSQL full schema — all tables with current column set. */\nexport const SCHEMA_SQL_POSTGRES =\n toPostgres(ALL_TABLES_FULL) +\n '\\nCREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY);\\n'\n\n/** MySQL full schema — all tables with current column set. */\nexport const SCHEMA_SQL_MYSQL =\n toMySQL(ALL_TABLES_FULL) +\n '\\nCREATE TABLE IF NOT EXISTS schema_version (\\n version INT NOT NULL PRIMARY KEY\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\\n'\n\n// ── Core data ─────────────────────────────────────────────────────────────────\n\nexport interface SeedPermission { id: string; name: string; description: string }\nexport interface SeedRole { id: string; tenantId: string; name: string; description: string }\nexport interface SeedRolePermission { roleId: string; permissionId: string }\n\nexport const SEED_TENANT = { id: 'default', name: 'Default', slug: 'default' } as const\n\nexport const SEED_PERMISSIONS: SeedPermission[] = [\n { id: 'perm_profile_create', name: 'profile:create', description: 'Create profiles' },\n { id: 'perm_profile_update', name: 'profile:update', description: 'Update profiles' },\n { id: 'perm_profile_delete', name: 'profile:delete', description: 'Delete profiles' },\n { id: 'perm_profile_view', name: 'profile:view', description: 'View profiles' },\n { id: 'perm_consent_view', name: 'consent:view', description: 'View consent records' },\n { id: 'perm_visitor_view', name: 'visitor:view', description: 'View visitor records' },\n { id: 'perm_user_create', name: 'user:create', description: 'Create users' },\n { id: 'perm_user_update', name: 'user:update', description: 'Update users' },\n { id: 'perm_user_delete', name: 'user:delete', description: 'Delete users' },\n { id: 'perm_user_view', name: 'user:view', description: 'View users' },\n { id: 'perm_role_create', name: 'role:create', description: 'Create roles' },\n { id: 'perm_role_update', name: 'role:update', description: 'Update roles' },\n { id: 'perm_role_delete', name: 'role:delete', description: 'Delete roles' },\n { id: 'perm_role_view', name: 'role:view', description: 'View roles' },\n { id: 'perm_audit_view', name: 'audit:view', description: 'View audit logs' },\n { id: 'perm_settings_update', name: 'settings:update', description: 'Update settings' },\n { id: 'perm_export_run', name: 'export:run', description: 'Run data exports' },\n]\n\nexport const SEED_ROLES: SeedRole[] = [\n { id: 'role_super_admin', tenantId: 'default', name: 'super_admin', description: 'Full system access' },\n { id: 'role_admin', tenantId: 'default', name: 'admin', description: 'Administrative access' },\n { id: 'role_compliance_officer', tenantId: 'default', name: 'compliance_officer', description: 'Compliance read access' },\n { id: 'role_viewer', tenantId: 'default', name: 'viewer', description: 'Read-only access' },\n]\n\nconst adminExcluded = new Set(['role:delete', 'user:delete'])\nconst complianceAllowed = new Set(['consent:view', 'visitor:view', 'audit:view', 'profile:view', 'export:run'])\nconst viewerAllowed = new Set(['consent:view', 'visitor:view', 'profile:view'])\n\nexport const SEED_ROLE_PERMISSIONS: SeedRolePermission[] = [\n ...SEED_PERMISSIONS.map(p => ({ roleId: 'role_super_admin', permissionId: p.id })),\n ...SEED_PERMISSIONS.filter(p => !adminExcluded.has(p.name)).map(p => ({ roleId: 'role_admin', permissionId: p.id })),\n ...SEED_PERMISSIONS.filter(p => complianceAllowed.has(p.name)).map(p => ({ roleId: 'role_compliance_officer', permissionId: p.id })),\n ...SEED_PERMISSIONS.filter(p => viewerAllowed.has(p.name)).map(p => ({ roleId: 'role_viewer', permissionId: p.id })),\n]\n\n// ── SQLite seed SQL ───────────────────────────────────────────────────────────\n// Generated from seed data above — idempotent via INSERT OR IGNORE.\n// MongoDB, MySQL, PostgreSQL adapters use the typed JS objects directly.\n\nfunction q(s: string): string { return `'${s.replace(/'/g, \"''\")}'` }\n\nfunction generateSeedSQL(): string {\n const { id, name, slug } = SEED_TENANT\n const tenant = `INSERT OR IGNORE INTO tenants (id, name, slug) VALUES (${q(id)},${q(name)},${q(slug)});`\n\n const perms = `INSERT OR IGNORE INTO permissions (id, name, description) VALUES\\n${\n SEED_PERMISSIONS.map(p => ` (${q(p.id)},${q(p.name)},${q(p.description)})`).join(',\\n')\n };`\n\n const rolesSQL = `INSERT OR IGNORE INTO roles (id, tenant_id, name, description) VALUES\\n${\n SEED_ROLES.map(r => ` (${q(r.id)},${q(r.tenantId)},${q(r.name)},${q(r.description)})`).join(',\\n')\n };`\n\n const byRole = new Map<string, string[]>()\n for (const rp of SEED_ROLE_PERMISSIONS) {\n if (!byRole.has(rp.roleId)) byRole.set(rp.roleId, [])\n byRole.get(rp.roleId)!.push(rp.permissionId)\n }\n const rolePerms = [...byRole.entries()].map(([roleId, permIds]) =>\n `INSERT OR IGNORE INTO role_permissions (role_id, permission_id) VALUES\\n${\n permIds.map(pid => ` (${q(roleId)},${q(pid)})`).join(',\\n')\n };`\n ).join('\\n')\n\n return [tenant, perms, rolesSQL, rolePerms].join('\\n')\n}\n\nexport const SEED_SQL = generateSeedSQL()\n","import type { Database as DB, Statement } from 'better-sqlite3'\n\nconst MAX_PAGE_SIZE = 500\nimport { randomUUID, randomProfileId } from '../../utils/crypto'\nimport {\n SEED_SQL,\n SCHEMA_SQL_SQLITE,\n SCHEMA_SQL_SQLITE_API_KEYS,\n SCHEMA_SQL_SQLITE_ENTERPRISE,\n SCHEMA_SQL_SQLITE_TEMPLATES,\n} from '../seed-data'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n ProfileConfig,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n ServerCookieTemplate,\n ServerUITemplate,\n CreateCookieTemplateInput,\n UpdateCookieTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n} from '@consenti/types'\n\ninterface RawProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RawConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n profile_version: number; locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n}\ninterface RawConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RawVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RawUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n}\ninterface RawRole {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RawPermission {\n id: string; name: string; description: string | null\n}\ninterface RawAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RawTenant {\n id: string; name: string; slug: string; created_at: string; updated_at: string\n}\ninterface RawApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number; created_at: string\n}\n\ninterface RawCookieTemplate {\n id: string; tenant_id: string; name: string; cookies_json: string; created_at: string; updated_at: string\n}\ninterface RawUITemplate {\n id: string; tenant_id: string; name: string; settings_json: string; created_at: string; updated_at: string\n}\n\n\ntype Stmt = Statement\n\nexport class BetterSqlite3Adapter implements StorageAdapter {\n private db!: DB\n private cache = new Map<string, Stmt>()\n\n constructor(private config: StorageConfig) {}\n\n private stmt(sql: string): Stmt {\n let s = this.cache.get(sql)\n if (!s) { s = this.db.prepare(sql); this.cache.set(sql, s) }\n return s\n }\n\n async connect(): Promise<void> {\n // Lazy: only loaded when this driver is selected — keeps better-sqlite3\n // as an optional peer dep without crashing startup for other drivers.\n const mod = await import('better-sqlite3').catch(() => {\n throw new Error(\"driver 'better-sqlite3' requires: npm install better-sqlite3\")\n }) as { default: new(path: string) => DB }\n this.db = new mod.default(this.config.path ?? './consenti.db')\n this.db.exec('PRAGMA journal_mode=WAL')\n this.db.exec('PRAGMA foreign_keys=ON')\n this.db.exec('PRAGMA synchronous=NORMAL')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n this.cache.clear()\n this.db.close()\n }\n\n async migrate(): Promise<void> {\n const row = this.db.prepare('PRAGMA user_version').get() as { user_version: number }\n if (row.user_version < 1) {\n this.db.exec(SCHEMA_SQL_SQLITE)\n this.db.exec(SEED_SQL)\n this.db.exec('PRAGMA user_version = 1')\n }\n if (row.user_version < 2) {\n this.db.exec(SCHEMA_SQL_SQLITE_API_KEYS)\n this.db.exec('PRAGMA user_version = 2')\n }\n if (row.user_version < 3) {\n this.db.exec(SCHEMA_SQL_SQLITE_ENTERPRISE)\n this.db.exec('PRAGMA user_version = 3')\n }\n if (row.user_version < 4) {\n this.db.exec('ALTER TABLE user_roles ADD COLUMN tenant_id TEXT NOT NULL DEFAULT \\'default\\'')\n this.db.exec('PRAGMA user_version = 4')\n }\n if (row.user_version < 5) {\n this.db.exec(SCHEMA_SQL_SQLITE_TEMPLATES)\n this.db.exec('PRAGMA user_version = 5')\n }\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n private mapProfile(row: RawProfile): Profile {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n defaultLocale: row.default_locale,\n version: row.version,\n profileJson: JSON.parse(row.profile_json) as ProfileConfig,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO profiles (id,tenant_id,name,default_locale,profile_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.defaultLocale, JSON.stringify(data.profileJson), now, now)\n const profile = await this.getProfile(id)\n if (!profile) throw new Error('Failed to create profile')\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const existing = await this.getProfile(id)\n if (!existing) throw new Error(`Profile ${id} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'version = version + 1']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.defaultLocale != null) { fields.push('default_locale = ?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { fields.push('profile_json = ?'); vals.push(JSON.stringify(data.profileJson)) }\n vals.push(id)\n this.stmt(`UPDATE profiles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const updated = await this.getProfile(id)\n if (!updated) throw new Error('Failed to update profile')\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.stmt('DELETE FROM profiles WHERE id = ?').run(id)\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const row = this.stmt('SELECT * FROM profiles WHERE id = ?').get(id) as RawProfile | undefined\n return row ? this.mapProfile(row) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = this.stmt('SELECT * FROM profiles WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawProfile[]\n return rows.map(r => this.mapProfile(r))\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n private mapConsent(row: RawConsent): ConsentDbRecord {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n profileId: row.profile_id,\n profileVersion: row.profile_version,\n locale: row.locale,\n consentJson: JSON.parse(row.consent_json) as ConsentValue,\n gpcDetected: row.gpc_detected === 1,\n source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n ...(row.parental_consent_token != null ? { parentalConsentToken: row.parental_consent_token } : {}),\n ...(row.tcf_string != null ? { tcfString: row.tcf_string } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO consent_records (id,tenant_id,visitor_id,profile_id,profile_version,locale,consent_json,gpc_detected,source,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.profileId, data.profileVersion, data.locale, JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source, now, now)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, data.tenantId, id, data.visitorId, null, JSON.stringify(data.consentJson), 'created', now)\n const record = await this.getConsent(data.visitorId)\n if (!record) throw new Error('Failed to create consent')\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'consent_json = ?']\n const vals: (string | number | null)[] = [now, JSON.stringify(data.consentJson)]\n if (data.locale != null) { fields.push('locale = ?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { fields.push('gpc_detected = ?'); vals.push(data.gpcDetected ? 1 : 0) }\n vals.push(visitorId)\n this.stmt(`UPDATE consent_records SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, existing.tenantId, existing.id, visitorId, JSON.stringify(existing.consentJson), JSON.stringify(data.consentJson), 'updated', now)\n const updated = await this.getConsent(visitorId)\n if (!updated) throw new Error('Failed to update consent')\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitorId)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitorId)\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const row = this.stmt('SELECT * FROM consent_records WHERE visitor_id = ?').get(visitorId) as RawConsent | undefined\n return row ? this.mapConsent(row) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.profileId) { conditions.push('profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n vals.push(limit, offset)\n const rows = this.stmt(\n `SELECT * FROM consent_records WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals) as unknown as RawConsent[]\n return rows.map(r => this.mapConsent(r))\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n 'SELECT * FROM consent_records WHERE tenant_id = ? LIMIT ? OFFSET ?'\n ).all(filters.tenantId, pageSize, offset) as unknown as RawConsent[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapConsent(row)\n offset += pageSize\n }\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = this.stmt(\n 'SELECT * FROM consent_history WHERE visitor_id = ? ORDER BY created_at ASC'\n ).all(visitorId) as unknown as RawConsentHistory[]\n return rows.map(r => ({\n id: r.id,\n tenantId: r.tenant_id,\n consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json != null ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'],\n createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n private mapVisitor(row: RawVisitor): Visitor {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n firstSeen: row.first_seen,\n lastSeen: row.last_seen,\n ...(row.country != null ? { country: row.country } : {}),\n ...(row.region != null ? { region: row.region } : {}),\n ...(row.city != null ? { city: row.city } : {}),\n ...(row.ip_hash != null ? { ipHash: row.ip_hash } : {}),\n ...(row.user_agent_hash != null ? { userAgentHash: row.user_agent_hash } : {}),\n }\n }\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO visitors (id,tenant_id,visitor_id,country,region,city,ip_hash,user_agent_hash,first_seen,last_seen) VALUES (?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.country ?? null, data.region ?? null, data.city ?? null, data.ipHash ?? null, data.userAgentHash ?? null, now, now)\n const visitor = await this.getVisitor(data.visitorId)\n if (!visitor) throw new Error('Failed to create visitor')\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const fields: string[] = ['last_seen = ?']\n const vals: (string | number | null)[] = [data.lastSeen ?? now]\n if (data.country != null) { fields.push('country = ?'); vals.push(data.country) }\n if (data.region != null) { fields.push('region = ?'); vals.push(data.region) }\n if (data.city != null) { fields.push('city = ?'); vals.push(data.city) }\n vals.push(visitorId)\n this.stmt(`UPDATE visitors SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const visitor = await this.getVisitor(visitorId)\n if (!visitor) throw new Error(`Visitor ${visitorId} not found`)\n return visitor\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM visitors WHERE visitor_id = ?').run(visitorId)\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const row = this.stmt('SELECT * FROM visitors WHERE visitor_id = ?').get(visitorId) as RawVisitor | undefined\n return row ? this.mapVisitor(row) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.from) { conditions.push('first_seen >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('first_seen <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n vals.push(limit, offset)\n const rows = this.stmt(\n `SELECT * FROM visitors WHERE ${conditions.join(' AND ')} ORDER BY last_seen DESC LIMIT ? OFFSET ?`\n ).all(...vals) as unknown as RawVisitor[]\n return rows.map(r => this.mapVisitor(r))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────\n\n private mapUser(row: RawUser): AdminUser {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n email: row.email,\n passwordHash: row.password_hash,\n isActive: row.is_active === 1,\n totpEnabled: row.totp_enabled === 1,\n ...(row.totp_secret != null ? { totpSecret: row.totp_secret } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO users (id,tenant_id,name,email,password_hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.email, data.passwordHash, now, now)\n const user = await this.getUserByEmail(data.email)\n if (!user) throw new Error('Failed to create user')\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.email != null) { fields.push('email = ?'); vals.push(data.email) }\n if (data.passwordHash != null) { fields.push('password_hash = ?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { fields.push('is_active = ?'); vals.push(data.isActive ? 1 : 0) }\n if ('totpSecret' in data) { fields.push('totp_secret = ?'); vals.push(data.totpSecret ?? null) }\n if (data.totpEnabled != null) { fields.push('totp_enabled = ?'); vals.push(data.totpEnabled ? 1 : 0) }\n vals.push(id)\n this.stmt(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n if (!row) throw new Error(`User ${id} not found`)\n return this.mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n this.stmt('DELETE FROM user_roles WHERE user_id = ?').run(id)\n this.stmt('DELETE FROM users WHERE id = ?').run(id)\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE email = ?').get(email) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = this.stmt('SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawUser[]\n return rows.map(r => this.mapUser(r))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const row = tenantId\n ? this.stmt('SELECT COUNT(*) as n FROM users WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n : this.stmt('SELECT COUNT(*) as n FROM users').get() as { n: number } | undefined\n return row?.n ?? 0\n }\n\n // ── Roles & Permissions ───────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = this.stmt('SELECT * FROM roles WHERE tenant_id = ?').all(tenantId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n this.stmt('INSERT INTO roles (id,tenant_id,name,description) VALUES (?,?,?,?)').run(\n id, data.tenantId, data.name, data.description ?? null\n )\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error('Failed to create role')\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const fields: string[] = []\n const vals: (string | number | null)[] = []\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.description != null) { fields.push('description = ?'); vals.push(data.description) }\n if (fields.length > 0) {\n vals.push(id)\n this.stmt(`UPDATE roles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n }\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error(`Role ${id} not found`)\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async deleteRole(id: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM user_roles WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM roles WHERE id = ?').run(id)\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = this.stmt('SELECT * FROM permissions ORDER BY name ASC').all() as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)').run(roleId, permissionId)\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?').run(roleId, permissionId)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = this.stmt(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = ?'\n ).all(roleId) as unknown as RawPermission[]\n return rows.map(r => ({\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ? AND ur.tenant_id = ?'\n ).all(userId, tenantId) as unknown as RawRole[]\n : this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ?'\n ).all(userId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)').run(userId, roleId, tenantId)\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ? AND tenant_id = ?').run(userId, roleId, tenantId)\n } else {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?').run(userId, roleId)\n }\n }\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO audit_logs (id,tenant_id,user_id,action,resource_type,resource_id,old_data,new_data,created_at) VALUES (?,?,?,?,?,?,?,?,?)'\n ).run(\n id, data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null,\n now\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.resourceType) { conditions.push('resource_type = ?'); vals.push(filters.resourceType) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n vals.push(limit, offset)\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals) as unknown as RawAuditLog[]\n return rows.map(r => this.mapAuditLog(r))\n }\n\n private mapAuditLog(r: RawAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action,\n resourceType: r.resource_type, createdAt: r.created_at,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n }\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, pageSize, offset) as unknown as RawAuditLog[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapAuditLog(row)\n offset += pageSize\n }\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const tc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const gc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const vc = this.stmt('SELECT COUNT(*) as n FROM visitors WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const totalConsents = tc?.n ?? 0\n\n const recent = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 500'\n ).all(tenantId) as unknown as { consent_json: string }[]\n\n let accepted = 0, rejected = 0\n for (const r of recent) {\n const vals = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n\n return {\n totalConsents,\n gpcDetectedCount: gc?.n ?? 0,\n totalVisitors: vc?.n ?? 0,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? LIMIT 5000'\n ).all(tenantId) as unknown as { consent_json: string }[]\n const stats: CategoryStats = {}\n for (const row of rows) {\n const consent = JSON.parse(row.consent_json) as Record<string, string>\n for (const [key, status] of Object.entries(consent)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const rows = this.stmt(\n `SELECT date(created_at) as date, COUNT(*) as count FROM consent_records WHERE tenant_id = ? AND created_at >= date('now', ?) GROUP BY date(created_at) ORDER BY date ASC`\n ).all(tenantId, `-${days} days`) as unknown as { date: string; count: number }[]\n return rows.map(r => ({ date: r.date, count: r.count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = this.stmt(\n `SELECT v.country, COUNT(*) as count FROM consent_records cr JOIN visitors v ON v.visitor_id = cr.visitor_id WHERE cr.tenant_id = ? AND v.country IS NOT NULL GROUP BY v.country ORDER BY count DESC LIMIT 50`\n ).all(tenantId) as unknown as { country: string; count: number }[]\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const detected = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const t = total?.n ?? 0\n const d = detected?.n ?? 0\n return { detected: d, total: t, rate: t > 0 ? Math.round((d / t) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = this.stmt('SELECT * FROM tenants ORDER BY name ASC').all() as unknown as RawTenant[]\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO tenants (id,name,slug,created_at,updated_at) VALUES (?,?,?,?,?)').run(id, data.name, data.slug, now, now)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error('Failed to create tenant')\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.slug != null) { fields.push('slug = ?'); vals.push(data.slug) }\n vals.push(id)\n this.stmt(`UPDATE tenants SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error(`Tenant ${id} not found`)\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.stmt('DELETE FROM tenants WHERE id = ?').run(id)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = this.stmt(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?'\n ).all(cutoff) as unknown as { visitor_id: string }[]\n for (const { visitor_id } of toDelete) {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitor_id)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitor_id)\n }\n return toDelete.length\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RawApiKey): ApiKey {\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1, createdAt: r.created_at }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO api_keys (id,tenant_id,key_hash,name,created_at) VALUES (?,?,?,?,?)').run(id, data.tenantId, data.keyHash, data.name, now)\n const row = this.stmt('SELECT * FROM api_keys WHERE id = ?').get(id) as RawApiKey | undefined\n if (!row) throw new Error('Failed to create API key')\n return this.mapApiKey(row)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const row = this.stmt('SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) as RawApiKey | undefined\n return row ? this.mapApiKey(row) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n this.stmt('UPDATE api_keys SET is_active = 0 WHERE id = ?').run(id)\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = this.stmt('SELECT * FROM api_keys WHERE tenant_id = ? ORDER BY created_at DESC').all(tenantId) as unknown as RawApiKey[]\n return rows.map(r => this.mapApiKey(r))\n }\n\n // ── Cookie Templates ───────────────────────────────────────────────────────\n\n private mapCookieTemplate(r: RawCookieTemplate): ServerCookieTemplate {\n return { id: r.id, tenantId: r.tenant_id, name: r.name, cookies: JSON.parse(r.cookies_json), createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createCookieTemplate(data: CreateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO cookie_templates (id,tenant_id,name,cookies_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run(id, data.tenantId, data.name, JSON.stringify(data.cookies), now, now)\n return this.mapCookieTemplate(this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as unknown as RawCookieTemplate)\n }\n\n async updateCookieTemplate(id: string, data: UpdateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const now = new Date().toISOString()\n if (data.name !== undefined) this.stmt('UPDATE cookie_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n if (data.cookies !== undefined) this.stmt('UPDATE cookie_templates SET cookies_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(data.cookies), now, id)\n const row = this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as RawCookieTemplate | undefined\n if (!row) throw new Error('Cookie template not found')\n return this.mapCookieTemplate(row)\n }\n\n async deleteCookieTemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM cookie_templates WHERE id = ?').run(id)\n }\n\n async getCookieTemplate(id: string): Promise<ServerCookieTemplate | null> {\n const row = this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as RawCookieTemplate | undefined\n return row ? this.mapCookieTemplate(row) : null\n }\n\n async getCookieTemplates(tenantId: string): Promise<ServerCookieTemplate[]> {\n const rows = this.stmt('SELECT * FROM cookie_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawCookieTemplate[]\n return rows.map(r => this.mapCookieTemplate(r))\n }\n\n async copyCookieTemplate(id: string, newName: string): Promise<ServerCookieTemplate> {\n const src = await this.getCookieTemplate(id)\n if (!src) throw new Error('Cookie template not found')\n return this.createCookieTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n private mapUITemplate(r: RawUITemplate): ServerUITemplate {\n const s = JSON.parse(r.settings_json) as Omit<ServerUITemplate, 'id' | 'tenantId' | 'name' | 'createdAt' | 'updatedAt'>\n return { id: r.id, tenantId: r.tenant_id, name: r.name, ...s, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n this.stmt('INSERT INTO ui_templates (id,tenant_id,name,settings_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run(id, tenantId, name, JSON.stringify(settings), now, now)\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n if (!row) throw new Error('UI template not found')\n if (data.name !== undefined) this.stmt('UPDATE ui_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n const { name: _n, ...settingsUpdate } = data\n if (Object.keys(settingsUpdate).length > 0) {\n const current = JSON.parse(row.settings_json) as Record<string, unknown>\n const merged = { ...current, ...settingsUpdate }\n this.stmt('UPDATE ui_templates SET settings_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(merged), now, id)\n }\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM ui_templates WHERE id = ?').run(id)\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n return row ? this.mapUITemplate(row) : null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n const rows = this.stmt('SELECT * FROM ui_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawUITemplate[]\n return rows.map(r => this.mapUITemplate(r))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src\n return this.createUITemplate({ tenantId, name: newName, mainBanner, gpcBanner, preferenceModal })\n }\n}\n","import type { Database as DB, Statement } from 'node-sqlite3-wasm'\n\nconst MAX_PAGE_SIZE = 500\nimport { randomUUID, randomProfileId } from '../../utils/crypto'\nimport {\n SEED_SQL,\n SCHEMA_SQL_SQLITE,\n SCHEMA_SQL_SQLITE_API_KEYS,\n SCHEMA_SQL_SQLITE_ENTERPRISE,\n SCHEMA_SQL_SQLITE_TEMPLATES,\n} from '../seed-data'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n ProfileConfig,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n ServerCookieTemplate,\n ServerUITemplate,\n CreateCookieTemplateInput,\n UpdateCookieTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n} from '@consenti/types'\n\ninterface RawProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RawConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n profile_version: number; locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n}\ninterface RawConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RawVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RawUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n}\ninterface RawRole {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RawPermission {\n id: string; name: string; description: string | null\n}\ninterface RawAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RawTenant {\n id: string; name: string; slug: string; created_at: string; updated_at: string\n}\ninterface RawApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number; created_at: string\n}\ninterface RawCookieTemplate {\n id: string; tenant_id: string; name: string; cookies_json: string; created_at: string; updated_at: string\n}\ninterface RawUITemplate {\n id: string; tenant_id: string; name: string; settings_json: string; created_at: string; updated_at: string\n}\n\n\n// node-sqlite3-wasm statements are NOT garbage-collected — they must be\n// finalized explicitly. All cached statements are finalized in disconnect().\n// Parameter binding uses run(array) / all(array) — not spread — per the WASM API.\n\ntype Val = string | number | null\n\nexport class NodeSqlite3WasmAdapter implements StorageAdapter {\n private db!: DB\n private cache = new Map<string, Statement>()\n\n constructor(private config: StorageConfig) { }\n\n private stmt(sql: string): Statement {\n let s = this.cache.get(sql)\n if (!s) { s = this.db.prepare(sql); this.cache.set(sql, s) }\n return s\n }\n\n async connect(): Promise<void> {\n // Lazy: only loaded when this driver is actually selected — allows the package\n // to be an optional peer dep without crashing on startup for other drivers.\n const mod = await import('node-sqlite3-wasm').catch(() => {\n throw new Error(\"driver 'node-sqlite3-wasm' requires: npm install node-sqlite3-wasm\")\n }) as { default: { Database: new (path: string) => DB } }\n this.db = new mod.default.Database(this.config.path ?? './consenti.db')\n this.db.exec('PRAGMA journal_mode=WAL')\n this.db.exec('PRAGMA foreign_keys=ON')\n this.db.exec('PRAGMA synchronous=NORMAL')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n // Finalize all prepared statements before closing — WASM objects are not\n // GC'd and will leak if not explicitly freed.\n for (const stmt of this.cache.values()) {\n stmt.finalize()\n }\n this.cache.clear()\n this.db.close()\n }\n\n async migrate(): Promise<void> {\n const row = this.stmt('PRAGMA user_version').get() as { user_version: number } | null\n const ver = row?.user_version ?? 0\n if (ver < 1) {\n this.db.exec(SCHEMA_SQL_SQLITE)\n this.db.exec(SEED_SQL)\n this.db.exec('PRAGMA user_version = 1')\n }\n if (ver < 2) {\n this.db.exec(SCHEMA_SQL_SQLITE_API_KEYS)\n this.db.exec('PRAGMA user_version = 2')\n }\n if (ver < 3) {\n this.db.exec(SCHEMA_SQL_SQLITE_ENTERPRISE)\n this.db.exec('PRAGMA user_version = 3')\n }\n if (ver < 4) {\n this.db.exec('ALTER TABLE user_roles ADD COLUMN tenant_id TEXT NOT NULL DEFAULT \\'default\\'')\n this.db.exec('PRAGMA user_version = 4')\n }\n if (ver < 5) {\n this.db.exec(SCHEMA_SQL_SQLITE_TEMPLATES)\n this.db.exec('PRAGMA user_version = 5')\n }\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n private mapProfile(row: RawProfile): Profile {\n return {\n id: row.id, tenantId: row.tenant_id, name: row.name,\n defaultLocale: row.default_locale, version: row.version,\n profileJson: JSON.parse(row.profile_json) as ProfileConfig,\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO profiles (id,tenant_id,name,default_locale,profile_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.name, data.defaultLocale, JSON.stringify(data.profileJson), now, now])\n const profile = await this.getProfile(id)\n if (!profile) throw new Error('Failed to create profile')\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const existing = await this.getProfile(id)\n if (!existing) throw new Error(`Profile ${id} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'version = version + 1']\n const vals: Val[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.defaultLocale != null) { fields.push('default_locale = ?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { fields.push('profile_json = ?'); vals.push(JSON.stringify(data.profileJson)) }\n vals.push(id)\n this.stmt(`UPDATE profiles SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n const updated = await this.getProfile(id)\n if (!updated) throw new Error('Failed to update profile')\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.stmt('DELETE FROM profiles WHERE id = ?').run(id)\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const row = this.stmt('SELECT * FROM profiles WHERE id = ?').get(id) as RawProfile | null\n return row ? this.mapProfile(row) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = this.stmt('SELECT * FROM profiles WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawProfile[]\n return rows.map(r => this.mapProfile(r))\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n private mapConsent(row: RawConsent): ConsentDbRecord {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id,\n profileId: row.profile_id, profileVersion: row.profile_version,\n locale: row.locale, consentJson: JSON.parse(row.consent_json) as ConsentValue,\n gpcDetected: row.gpc_detected === 1,\n source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n ...(row.parental_consent_token != null ? { parentalConsentToken: row.parental_consent_token } : {}),\n ...(row.tcf_string != null ? { tcfString: row.tcf_string } : {}),\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO consent_records (id,tenant_id,visitor_id,profile_id,profile_version,locale,consent_json,gpc_detected,source,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.visitorId, data.profileId, data.profileVersion, data.locale, JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source, now, now])\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run([histId, data.tenantId, id, data.visitorId, null, JSON.stringify(data.consentJson), 'created', now])\n const record = await this.getConsent(data.visitorId)\n if (!record) throw new Error('Failed to create consent')\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'consent_json = ?']\n const vals: Val[] = [now, JSON.stringify(data.consentJson)]\n if (data.locale != null) { fields.push('locale = ?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { fields.push('gpc_detected = ?'); vals.push(data.gpcDetected ? 1 : 0) }\n vals.push(visitorId)\n this.stmt(`UPDATE consent_records SET ${fields.join(', ')} WHERE visitor_id = ?`).run(vals)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run([histId, existing.tenantId, existing.id, visitorId, JSON.stringify(existing.consentJson), JSON.stringify(data.consentJson), 'updated', now])\n const updated = await this.getConsent(visitorId)\n if (!updated) throw new Error('Failed to update consent')\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitorId)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitorId)\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const row = this.stmt('SELECT * FROM consent_records WHERE visitor_id = ?').get(visitorId) as RawConsent | null\n return row ? this.mapConsent(row) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.profileId) { conditions.push('profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n const rows = this.stmt(\n `SELECT * FROM consent_records WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all([...vals, limit, offset]) as unknown as RawConsent[]\n return rows.map(r => this.mapConsent(r))\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n 'SELECT * FROM consent_records WHERE tenant_id = ? LIMIT ? OFFSET ?'\n ).all([filters.tenantId, pageSize, offset]) as unknown as RawConsent[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapConsent(row)\n offset += pageSize\n }\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = this.stmt(\n 'SELECT * FROM consent_history WHERE visitor_id = ? ORDER BY created_at ASC'\n ).all(visitorId) as unknown as RawConsentHistory[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json != null ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'],\n createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n private mapVisitor(row: RawVisitor): Visitor {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id,\n firstSeen: row.first_seen, lastSeen: row.last_seen,\n ...(row.country != null ? { country: row.country } : {}),\n ...(row.region != null ? { region: row.region } : {}),\n ...(row.city != null ? { city: row.city } : {}),\n ...(row.ip_hash != null ? { ipHash: row.ip_hash } : {}),\n ...(row.user_agent_hash != null ? { userAgentHash: row.user_agent_hash } : {}),\n }\n }\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO visitors (id,tenant_id,visitor_id,country,region,city,ip_hash,user_agent_hash,first_seen,last_seen) VALUES (?,?,?,?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.visitorId, data.country ?? null, data.region ?? null, data.city ?? null, data.ipHash ?? null, data.userAgentHash ?? null, now, now])\n const visitor = await this.getVisitor(data.visitorId)\n if (!visitor) throw new Error('Failed to create visitor')\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const fields: string[] = ['last_seen = ?']\n const vals: Val[] = [data.lastSeen ?? now]\n if (data.country != null) { fields.push('country = ?'); vals.push(data.country) }\n if (data.region != null) { fields.push('region = ?'); vals.push(data.region) }\n if (data.city != null) { fields.push('city = ?'); vals.push(data.city) }\n vals.push(visitorId)\n this.stmt(`UPDATE visitors SET ${fields.join(', ')} WHERE visitor_id = ?`).run(vals)\n const visitor = await this.getVisitor(visitorId)\n if (!visitor) throw new Error(`Visitor ${visitorId} not found`)\n return visitor\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM visitors WHERE visitor_id = ?').run(visitorId)\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const row = this.stmt('SELECT * FROM visitors WHERE visitor_id = ?').get(visitorId) as RawVisitor | null\n return row ? this.mapVisitor(row) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.from) { conditions.push('first_seen >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('first_seen <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n const rows = this.stmt(\n `SELECT * FROM visitors WHERE ${conditions.join(' AND ')} ORDER BY last_seen DESC LIMIT ? OFFSET ?`\n ).all([...vals, limit, offset]) as unknown as RawVisitor[]\n return rows.map(r => this.mapVisitor(r))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────\n\n private mapUser(row: RawUser): AdminUser {\n return {\n id: row.id, tenantId: row.tenant_id, name: row.name, email: row.email,\n passwordHash: row.password_hash, isActive: row.is_active === 1,\n totpEnabled: row.totp_enabled === 1,\n ...(row.totp_secret != null ? { totpSecret: row.totp_secret } : {}),\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO users (id,tenant_id,name,email,password_hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.name, data.email, data.passwordHash, now, now])\n const user = await this.getUserByEmail(data.email)\n if (!user) throw new Error('Failed to create user')\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: Val[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.email != null) { fields.push('email = ?'); vals.push(data.email) }\n if (data.passwordHash != null) { fields.push('password_hash = ?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { fields.push('is_active = ?'); vals.push(data.isActive ? 1 : 0) }\n if ('totpSecret' in data) { fields.push('totp_secret = ?'); vals.push(data.totpSecret ?? null) }\n if (data.totpEnabled != null) { fields.push('totp_enabled = ?'); vals.push(data.totpEnabled ? 1 : 0) }\n vals.push(id)\n this.stmt(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | null\n if (!row) throw new Error(`User ${id} not found`)\n return this.mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n this.stmt('DELETE FROM user_roles WHERE user_id = ?').run(id)\n this.stmt('DELETE FROM users WHERE id = ?').run(id)\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | null\n return row ? this.mapUser(row) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE email = ?').get(email) as RawUser | null\n return row ? this.mapUser(row) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = this.stmt('SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawUser[]\n return rows.map(r => this.mapUser(r))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const row = tenantId\n ? this.stmt('SELECT COUNT(*) as n FROM users WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n : this.stmt('SELECT COUNT(*) as n FROM users').get() as { n: number } | null\n return row?.n ?? 0\n }\n\n // ── Roles & Permissions ───────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = this.stmt('SELECT * FROM roles WHERE tenant_id = ?').all(tenantId) as unknown as RawRole[]\n return rows.map(r => ({ id: r.id, tenantId: r.tenant_id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n this.stmt('INSERT INTO roles (id,tenant_id,name,description) VALUES (?,?,?,?)').run(\n [id, data.tenantId, data.name, data.description ?? null]\n )\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | null\n if (!row) throw new Error('Failed to create role')\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const fields: string[] = []\n const vals: Val[] = []\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.description != null) { fields.push('description = ?'); vals.push(data.description) }\n if (fields.length > 0) {\n vals.push(id)\n this.stmt(`UPDATE roles SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n }\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | null\n if (!row) throw new Error(`Role ${id} not found`)\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async deleteRole(id: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM user_roles WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM roles WHERE id = ?').run(id)\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = this.stmt('SELECT * FROM permissions ORDER BY name ASC').all() as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)').run([roleId, permissionId])\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?').run([roleId, permissionId])\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = this.stmt(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = ?'\n ).all(roleId) as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ? AND ur.tenant_id = ?'\n ).all([userId, tenantId]) as unknown as RawRole[]\n : this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ?'\n ).all(userId) as unknown as RawRole[]\n return rows.map(r => ({ id: r.id, tenantId: r.tenant_id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)').run([userId, roleId, tenantId])\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ? AND tenant_id = ?').run([userId, roleId, tenantId])\n } else {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?').run([userId, roleId])\n }\n }\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO audit_logs (id,tenant_id,user_id,action,resource_type,resource_id,old_data,new_data,created_at) VALUES (?,?,?,?,?,?,?,?,?)'\n ).run([\n id, data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null,\n now,\n ])\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.resourceType) { conditions.push('resource_type = ?'); vals.push(filters.resourceType) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all([...vals, limit, offset]) as unknown as RawAuditLog[]\n return rows.map(r => this.mapAuditLog(r))\n }\n\n private mapAuditLog(r: RawAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action,\n resourceType: r.resource_type, createdAt: r.created_at,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n }\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all([...vals, pageSize, offset]) as unknown as RawAuditLog[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapAuditLog(row)\n offset += pageSize\n }\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const tc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n const gc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | null\n const vc = this.stmt('SELECT COUNT(*) as n FROM visitors WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n const totalConsents = tc?.n ?? 0\n\n const recent = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 500'\n ).all(tenantId) as unknown as { consent_json: string }[]\n\n let accepted = 0, rejected = 0\n for (const r of recent) {\n const vals = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n\n return {\n totalConsents,\n gpcDetectedCount: gc?.n ?? 0,\n totalVisitors: vc?.n ?? 0,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? LIMIT 5000'\n ).all(tenantId) as unknown as { consent_json: string }[]\n const stats: CategoryStats = {}\n for (const row of rows) {\n const consent = JSON.parse(row.consent_json) as Record<string, string>\n for (const [key, status] of Object.entries(consent)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const rows = this.stmt(\n `SELECT date(created_at) as date, COUNT(*) as count FROM consent_records WHERE tenant_id = ? AND created_at >= date('now', ?) GROUP BY date(created_at) ORDER BY date ASC`\n ).all([tenantId, `-${days} days`]) as unknown as { date: string; count: number }[]\n return rows.map(r => ({ date: r.date, count: r.count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = this.stmt(\n `SELECT v.country, COUNT(*) as count FROM consent_records cr JOIN visitors v ON v.visitor_id = cr.visitor_id WHERE cr.tenant_id = ? AND v.country IS NOT NULL GROUP BY v.country ORDER BY count DESC LIMIT 50`\n ).all(tenantId) as unknown as { country: string; count: number }[]\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n const detected = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | null\n const t = total?.n ?? 0\n const d = detected?.n ?? 0\n return { detected: d, total: t, rate: t > 0 ? Math.round((d / t) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = this.stmt('SELECT * FROM tenants ORDER BY name ASC').all() as unknown as RawTenant[]\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO tenants (id,name,slug,created_at,updated_at) VALUES (?,?,?,?,?)').run([id, data.name, data.slug, now, now])\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | null\n if (!row) throw new Error('Failed to create tenant')\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: Val[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.slug != null) { fields.push('slug = ?'); vals.push(data.slug) }\n vals.push(id)\n this.stmt(`UPDATE tenants SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | null\n if (!row) throw new Error(`Tenant ${id} not found`)\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.stmt('DELETE FROM tenants WHERE id = ?').run(id)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = this.stmt(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?'\n ).all(cutoff) as unknown as { visitor_id: string }[]\n for (const { visitor_id } of toDelete) {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitor_id)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitor_id)\n }\n return toDelete.length\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RawApiKey): ApiKey {\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1, createdAt: r.created_at }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO api_keys (id,tenant_id,key_hash,name,created_at) VALUES (?,?,?,?,?)').run([id, data.tenantId, data.keyHash, data.name, now])\n const row = this.stmt('SELECT * FROM api_keys WHERE id = ?').get(id) as RawApiKey | null\n if (!row) throw new Error('Failed to create API key')\n return this.mapApiKey(row)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const row = this.stmt('SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) as RawApiKey | null\n return row ? this.mapApiKey(row) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n this.stmt('UPDATE api_keys SET is_active = 0 WHERE id = ?').run(id)\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = this.stmt('SELECT * FROM api_keys WHERE tenant_id = ? ORDER BY created_at DESC').all(tenantId) as unknown as RawApiKey[]\n return rows.map(r => this.mapApiKey(r))\n }\n\n // ── Cookie Templates ───────────────────────────────────────────────────────\n\n private mapCookieTemplate(r: RawCookieTemplate): ServerCookieTemplate {\n return { id: r.id, tenantId: r.tenant_id, name: r.name, cookies: JSON.parse(r.cookies_json), createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createCookieTemplate(data: CreateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO cookie_templates (id,tenant_id,name,cookies_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run([id, data.tenantId, data.name, JSON.stringify(data.cookies), now, now])\n return this.mapCookieTemplate(this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as unknown as RawCookieTemplate)\n }\n\n async updateCookieTemplate(id: string, data: UpdateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const now = new Date().toISOString()\n if (data.name !== undefined) this.stmt('UPDATE cookie_templates SET name = ?, updated_at = ? WHERE id = ?').run([data.name, now, id])\n if (data.cookies !== undefined) this.stmt('UPDATE cookie_templates SET cookies_json = ?, updated_at = ? WHERE id = ?').run([JSON.stringify(data.cookies), now, id])\n const row = this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as RawCookieTemplate | null\n if (!row) throw new Error('Cookie template not found')\n return this.mapCookieTemplate(row)\n }\n\n async deleteCookieTemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM cookie_templates WHERE id = ?').run(id)\n }\n\n async getCookieTemplate(id: string): Promise<ServerCookieTemplate | null> {\n const row = this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as RawCookieTemplate | null\n return row ? this.mapCookieTemplate(row) : null\n }\n\n async getCookieTemplates(tenantId: string): Promise<ServerCookieTemplate[]> {\n const rows = this.stmt('SELECT * FROM cookie_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawCookieTemplate[]\n return rows.map(r => this.mapCookieTemplate(r))\n }\n\n async copyCookieTemplate(id: string, newName: string): Promise<ServerCookieTemplate> {\n const src = await this.getCookieTemplate(id)\n if (!src) throw new Error('Cookie template not found')\n return this.createCookieTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n private mapUITemplate(r: RawUITemplate): ServerUITemplate {\n const s = JSON.parse(r.settings_json) as Omit<ServerUITemplate, 'id' | 'tenantId' | 'name' | 'createdAt' | 'updatedAt'>\n return { id: r.id, tenantId: r.tenant_id, name: r.name, ...s, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n this.stmt('INSERT INTO ui_templates (id,tenant_id,name,settings_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run([id, tenantId, name, JSON.stringify(settings), now, now])\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | null\n if (!row) throw new Error('UI template not found')\n if (data.name !== undefined) this.stmt('UPDATE ui_templates SET name = ?, updated_at = ? WHERE id = ?').run([data.name, now, id])\n const { name: _n, ...settingsUpdate } = data\n if (Object.keys(settingsUpdate).length > 0) {\n const current = JSON.parse(row.settings_json) as Record<string, unknown>\n const merged = { ...current, ...settingsUpdate }\n this.stmt('UPDATE ui_templates SET settings_json = ?, updated_at = ? WHERE id = ?').run([JSON.stringify(merged), now, id])\n }\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM ui_templates WHERE id = ?').run(id)\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | null\n return row ? this.mapUITemplate(row) : null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n const rows = this.stmt('SELECT * FROM ui_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawUITemplate[]\n return rows.map(r => this.mapUITemplate(r))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src\n return this.createUITemplate({ tenantId, name: newName, mainBanner, gpcBanner, preferenceModal })\n }\n}\n","import type { DatabaseSync, StatementSync } from 'node:sqlite'\n\nconst MAX_PAGE_SIZE = 500\nimport { randomUUID, randomProfileId } from '../../utils/crypto'\nimport {\n SEED_SQL,\n SCHEMA_SQL_SQLITE,\n SCHEMA_SQL_SQLITE_API_KEYS,\n SCHEMA_SQL_SQLITE_ENTERPRISE,\n SCHEMA_SQL_SQLITE_TEMPLATES,\n} from '../seed-data'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n ProfileConfig,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n ServerCookieTemplate,\n ServerUITemplate,\n CreateCookieTemplateInput,\n UpdateCookieTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n} from '@consenti/types'\n\ninterface RawProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RawConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n profile_version: number; locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n}\ninterface RawConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RawVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RawUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n}\ninterface RawRole {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RawPermission {\n id: string; name: string; description: string | null\n}\ninterface RawAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RawTenant {\n id: string; name: string; slug: string; created_at: string; updated_at: string\n}\ninterface RawApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number; created_at: string\n}\ninterface RawCookieTemplate {\n id: string; tenant_id: string; name: string; cookies_json: string; created_at: string; updated_at: string\n}\ninterface RawUITemplate {\n id: string; tenant_id: string; name: string; settings_json: string; created_at: string; updated_at: string\n}\n\n\ntype Stmt = StatementSync\n\nexport class NodeSqliteBuiltinAdapter implements StorageAdapter {\n private db!: DatabaseSync\n private cache = new Map<string, Stmt>()\n\n constructor(private config: StorageConfig) {}\n\n private stmt(sql: string): Stmt {\n let s = this.cache.get(sql)\n if (!s) { s = this.db.prepare(sql); this.cache.set(sql, s) }\n return s\n }\n\n async connect(): Promise<void> {\n // Lazy: node:sqlite is only available on Node.js >= 22.5.0.\n // Loading it here (not at module load) prevents startup failure on Node 20.\n const { DatabaseSync: Db } = await import('node:sqlite').catch(() => {\n throw new Error(`driver 'node:sqlite' requires Node.js >= 22.5.0 (current: ${process.version})`)\n }) as { DatabaseSync: new(path: string) => DatabaseSync }\n this.db = new Db(this.config.path ?? './consenti.db')\n this.db.exec('PRAGMA journal_mode=WAL')\n this.db.exec('PRAGMA foreign_keys=ON')\n this.db.exec('PRAGMA synchronous=NORMAL')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n this.cache.clear()\n this.db.close()\n }\n\n async migrate(): Promise<void> {\n const row = this.db.prepare('PRAGMA user_version').get() as { user_version: number }\n if (row.user_version < 1) {\n this.db.exec(SCHEMA_SQL_SQLITE)\n this.db.exec(SEED_SQL)\n this.db.exec('PRAGMA user_version = 1')\n }\n if (row.user_version < 2) {\n this.db.exec(SCHEMA_SQL_SQLITE_API_KEYS)\n this.db.exec('PRAGMA user_version = 2')\n }\n if (row.user_version < 3) {\n this.db.exec(SCHEMA_SQL_SQLITE_ENTERPRISE)\n this.db.exec('PRAGMA user_version = 3')\n }\n if (row.user_version < 4) {\n this.db.exec('ALTER TABLE user_roles ADD COLUMN tenant_id TEXT NOT NULL DEFAULT \\'default\\'')\n this.db.exec('PRAGMA user_version = 4')\n }\n if (row.user_version < 5) {\n this.db.exec(SCHEMA_SQL_SQLITE_TEMPLATES)\n this.db.exec('PRAGMA user_version = 5')\n }\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n private mapProfile(row: RawProfile): Profile {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n defaultLocale: row.default_locale,\n version: row.version,\n profileJson: JSON.parse(row.profile_json) as ProfileConfig,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO profiles (id,tenant_id,name,default_locale,profile_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.defaultLocale, JSON.stringify(data.profileJson), now, now)\n const profile = await this.getProfile(id)\n if (!profile) throw new Error('Failed to create profile')\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const existing = await this.getProfile(id)\n if (!existing) throw new Error(`Profile ${id} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'version = version + 1']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.defaultLocale != null) { fields.push('default_locale = ?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { fields.push('profile_json = ?'); vals.push(JSON.stringify(data.profileJson)) }\n vals.push(id)\n this.stmt(`UPDATE profiles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const updated = await this.getProfile(id)\n if (!updated) throw new Error('Failed to update profile')\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.stmt('DELETE FROM profiles WHERE id = ?').run(id)\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const row = this.stmt('SELECT * FROM profiles WHERE id = ?').get(id) as RawProfile | undefined\n return row ? this.mapProfile(row) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = this.stmt('SELECT * FROM profiles WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawProfile[]\n return rows.map(r => this.mapProfile(r))\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n private mapConsent(row: RawConsent): ConsentDbRecord {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n profileId: row.profile_id,\n profileVersion: row.profile_version,\n locale: row.locale,\n consentJson: JSON.parse(row.consent_json) as ConsentValue,\n gpcDetected: row.gpc_detected === 1,\n source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n ...(row.parental_consent_token != null ? { parentalConsentToken: row.parental_consent_token } : {}),\n ...(row.tcf_string != null ? { tcfString: row.tcf_string } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO consent_records (id,tenant_id,visitor_id,profile_id,profile_version,locale,consent_json,gpc_detected,source,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.profileId, data.profileVersion, data.locale, JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source, now, now)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, data.tenantId, id, data.visitorId, null, JSON.stringify(data.consentJson), 'created', now)\n const record = await this.getConsent(data.visitorId)\n if (!record) throw new Error('Failed to create consent')\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'consent_json = ?']\n const vals: (string | number | null)[] = [now, JSON.stringify(data.consentJson)]\n if (data.locale != null) { fields.push('locale = ?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { fields.push('gpc_detected = ?'); vals.push(data.gpcDetected ? 1 : 0) }\n vals.push(visitorId)\n this.stmt(`UPDATE consent_records SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, existing.tenantId, existing.id, visitorId, JSON.stringify(existing.consentJson), JSON.stringify(data.consentJson), 'updated', now)\n const updated = await this.getConsent(visitorId)\n if (!updated) throw new Error('Failed to update consent')\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitorId)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitorId)\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const row = this.stmt('SELECT * FROM consent_records WHERE visitor_id = ?').get(visitorId) as RawConsent | undefined\n return row ? this.mapConsent(row) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.profileId) { conditions.push('profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n vals.push(limit, offset)\n const rows = this.stmt(\n `SELECT * FROM consent_records WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals) as unknown as RawConsent[]\n return rows.map(r => this.mapConsent(r))\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n 'SELECT * FROM consent_records WHERE tenant_id = ? LIMIT ? OFFSET ?'\n ).all(filters.tenantId, pageSize, offset) as unknown as RawConsent[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapConsent(row)\n offset += pageSize\n }\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = this.stmt(\n 'SELECT * FROM consent_history WHERE visitor_id = ? ORDER BY created_at ASC'\n ).all(visitorId) as unknown as RawConsentHistory[]\n return rows.map(r => ({\n id: r.id,\n tenantId: r.tenant_id,\n consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json != null ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'],\n createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n private mapVisitor(row: RawVisitor): Visitor {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n firstSeen: row.first_seen,\n lastSeen: row.last_seen,\n ...(row.country != null ? { country: row.country } : {}),\n ...(row.region != null ? { region: row.region } : {}),\n ...(row.city != null ? { city: row.city } : {}),\n ...(row.ip_hash != null ? { ipHash: row.ip_hash } : {}),\n ...(row.user_agent_hash != null ? { userAgentHash: row.user_agent_hash } : {}),\n }\n }\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO visitors (id,tenant_id,visitor_id,country,region,city,ip_hash,user_agent_hash,first_seen,last_seen) VALUES (?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.country ?? null, data.region ?? null, data.city ?? null, data.ipHash ?? null, data.userAgentHash ?? null, now, now)\n const visitor = await this.getVisitor(data.visitorId)\n if (!visitor) throw new Error('Failed to create visitor')\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const fields: string[] = ['last_seen = ?']\n const vals: (string | number | null)[] = [data.lastSeen ?? now]\n if (data.country != null) { fields.push('country = ?'); vals.push(data.country) }\n if (data.region != null) { fields.push('region = ?'); vals.push(data.region) }\n if (data.city != null) { fields.push('city = ?'); vals.push(data.city) }\n vals.push(visitorId)\n this.stmt(`UPDATE visitors SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const visitor = await this.getVisitor(visitorId)\n if (!visitor) throw new Error(`Visitor ${visitorId} not found`)\n return visitor\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM visitors WHERE visitor_id = ?').run(visitorId)\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const row = this.stmt('SELECT * FROM visitors WHERE visitor_id = ?').get(visitorId) as RawVisitor | undefined\n return row ? this.mapVisitor(row) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.from) { conditions.push('first_seen >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('first_seen <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n vals.push(limit, offset)\n const rows = this.stmt(\n `SELECT * FROM visitors WHERE ${conditions.join(' AND ')} ORDER BY last_seen DESC LIMIT ? OFFSET ?`\n ).all(...vals) as unknown as RawVisitor[]\n return rows.map(r => this.mapVisitor(r))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────\n\n private mapUser(row: RawUser): AdminUser {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n email: row.email,\n passwordHash: row.password_hash,\n isActive: row.is_active === 1,\n totpEnabled: row.totp_enabled === 1,\n ...(row.totp_secret != null ? { totpSecret: row.totp_secret } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO users (id,tenant_id,name,email,password_hash,created_at,updated_at) VALUES (?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.email, data.passwordHash, now, now)\n const user = await this.getUserByEmail(data.email)\n if (!user) throw new Error('Failed to create user')\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.email != null) { fields.push('email = ?'); vals.push(data.email) }\n if (data.passwordHash != null) { fields.push('password_hash = ?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { fields.push('is_active = ?'); vals.push(data.isActive ? 1 : 0) }\n if ('totpSecret' in data) { fields.push('totp_secret = ?'); vals.push(data.totpSecret ?? null) }\n if (data.totpEnabled != null) { fields.push('totp_enabled = ?'); vals.push(data.totpEnabled ? 1 : 0) }\n vals.push(id)\n this.stmt(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n if (!row) throw new Error(`User ${id} not found`)\n return this.mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n this.stmt('DELETE FROM user_roles WHERE user_id = ?').run(id)\n this.stmt('DELETE FROM users WHERE id = ?').run(id)\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE email = ?').get(email) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = this.stmt('SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawUser[]\n return rows.map(r => this.mapUser(r))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const row = tenantId\n ? this.stmt('SELECT COUNT(*) as n FROM users WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n : this.stmt('SELECT COUNT(*) as n FROM users').get() as { n: number } | undefined\n return row?.n ?? 0\n }\n\n // ── Roles & Permissions ───────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = this.stmt('SELECT * FROM roles WHERE tenant_id = ?').all(tenantId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n this.stmt('INSERT INTO roles (id,tenant_id,name,description) VALUES (?,?,?,?)').run(\n id, data.tenantId, data.name, data.description ?? null\n )\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error('Failed to create role')\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const fields: string[] = []\n const vals: (string | number | null)[] = []\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.description != null) { fields.push('description = ?'); vals.push(data.description) }\n if (fields.length > 0) {\n vals.push(id)\n this.stmt(`UPDATE roles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n }\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error(`Role ${id} not found`)\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async deleteRole(id: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM user_roles WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM roles WHERE id = ?').run(id)\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = this.stmt('SELECT * FROM permissions ORDER BY name ASC').all() as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)').run(roleId, permissionId)\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?').run(roleId, permissionId)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = this.stmt(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = ?'\n ).all(roleId) as unknown as RawPermission[]\n return rows.map(r => ({\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ? AND ur.tenant_id = ?'\n ).all(userId, tenantId) as unknown as RawRole[]\n : this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ?'\n ).all(userId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)').run(userId, roleId, tenantId)\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ? AND tenant_id = ?').run(userId, roleId, tenantId)\n } else {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?').run(userId, roleId)\n }\n }\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO audit_logs (id,tenant_id,user_id,action,resource_type,resource_id,old_data,new_data,created_at) VALUES (?,?,?,?,?,?,?,?,?)'\n ).run(\n id, data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null,\n now\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.resourceType) { conditions.push('resource_type = ?'); vals.push(filters.resourceType) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = ((filters.page ?? 1) - 1) * limit\n vals.push(limit, offset)\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals) as unknown as RawAuditLog[]\n return rows.map(r => this.mapAuditLog(r))\n }\n\n private mapAuditLog(r: RawAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action,\n resourceType: r.resource_type, createdAt: r.created_at,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n }\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, pageSize, offset) as unknown as RawAuditLog[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapAuditLog(row)\n offset += pageSize\n }\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const tc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const gc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const vc = this.stmt('SELECT COUNT(*) as n FROM visitors WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const totalConsents = tc?.n ?? 0\n\n const recent = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 500'\n ).all(tenantId) as unknown as { consent_json: string }[]\n\n let accepted = 0, rejected = 0\n for (const r of recent) {\n const vals = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n\n return {\n totalConsents,\n gpcDetectedCount: gc?.n ?? 0,\n totalVisitors: vc?.n ?? 0,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? LIMIT 5000'\n ).all(tenantId) as unknown as { consent_json: string }[]\n const stats: CategoryStats = {}\n for (const row of rows) {\n const consent = JSON.parse(row.consent_json) as Record<string, string>\n for (const [key, status] of Object.entries(consent)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const rows = this.stmt(\n `SELECT date(created_at) as date, COUNT(*) as count FROM consent_records WHERE tenant_id = ? AND created_at >= date('now', ?) GROUP BY date(created_at) ORDER BY date ASC`\n ).all(tenantId, `-${days} days`) as unknown as { date: string; count: number }[]\n return rows.map(r => ({ date: r.date, count: r.count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = this.stmt(\n `SELECT v.country, COUNT(*) as count FROM consent_records cr JOIN visitors v ON v.visitor_id = cr.visitor_id WHERE cr.tenant_id = ? AND v.country IS NOT NULL GROUP BY v.country ORDER BY count DESC LIMIT 50`\n ).all(tenantId) as unknown as { country: string; count: number }[]\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const detected = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const t = total?.n ?? 0\n const d = detected?.n ?? 0\n return { detected: d, total: t, rate: t > 0 ? Math.round((d / t) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = this.stmt('SELECT * FROM tenants ORDER BY name ASC').all() as unknown as RawTenant[]\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO tenants (id,name,slug,created_at,updated_at) VALUES (?,?,?,?,?)').run(id, data.name, data.slug, now, now)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error('Failed to create tenant')\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.slug != null) { fields.push('slug = ?'); vals.push(data.slug) }\n vals.push(id)\n this.stmt(`UPDATE tenants SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error(`Tenant ${id} not found`)\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.stmt('DELETE FROM tenants WHERE id = ?').run(id)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = this.stmt(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?'\n ).all(cutoff) as unknown as { visitor_id: string }[]\n for (const { visitor_id } of toDelete) {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitor_id)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitor_id)\n }\n return toDelete.length\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RawApiKey): ApiKey {\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1, createdAt: r.created_at }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO api_keys (id,tenant_id,key_hash,name,created_at) VALUES (?,?,?,?,?)').run(id, data.tenantId, data.keyHash, data.name, now)\n const row = this.stmt('SELECT * FROM api_keys WHERE id = ?').get(id) as RawApiKey | undefined\n if (!row) throw new Error('Failed to create API key')\n return this.mapApiKey(row)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const row = this.stmt('SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) as RawApiKey | undefined\n return row ? this.mapApiKey(row) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n this.stmt('UPDATE api_keys SET is_active = 0 WHERE id = ?').run(id)\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = this.stmt('SELECT * FROM api_keys WHERE tenant_id = ? ORDER BY created_at DESC').all(tenantId) as unknown as RawApiKey[]\n return rows.map(r => this.mapApiKey(r))\n }\n\n // ── Cookie Templates ───────────────────────────────────────────────────────\n\n private mapCookieTemplate(r: RawCookieTemplate): ServerCookieTemplate {\n return { id: r.id, tenantId: r.tenant_id, name: r.name, cookies: JSON.parse(r.cookies_json), createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createCookieTemplate(data: CreateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO cookie_templates (id,tenant_id,name,cookies_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run(id, data.tenantId, data.name, JSON.stringify(data.cookies), now, now)\n return this.mapCookieTemplate(this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as unknown as RawCookieTemplate)\n }\n\n async updateCookieTemplate(id: string, data: UpdateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const now = new Date().toISOString()\n if (data.name !== undefined) this.stmt('UPDATE cookie_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n if (data.cookies !== undefined) this.stmt('UPDATE cookie_templates SET cookies_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(data.cookies), now, id)\n const row = this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as RawCookieTemplate | undefined\n if (!row) throw new Error('Cookie template not found')\n return this.mapCookieTemplate(row)\n }\n\n async deleteCookieTemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM cookie_templates WHERE id = ?').run(id)\n }\n\n async getCookieTemplate(id: string): Promise<ServerCookieTemplate | null> {\n const row = this.stmt('SELECT * FROM cookie_templates WHERE id = ?').get(id) as RawCookieTemplate | undefined\n return row ? this.mapCookieTemplate(row) : null\n }\n\n async getCookieTemplates(tenantId: string): Promise<ServerCookieTemplate[]> {\n const rows = this.stmt('SELECT * FROM cookie_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawCookieTemplate[]\n return rows.map(r => this.mapCookieTemplate(r))\n }\n\n async copyCookieTemplate(id: string, newName: string): Promise<ServerCookieTemplate> {\n const src = await this.getCookieTemplate(id)\n if (!src) throw new Error('Cookie template not found')\n return this.createCookieTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n private mapUITemplate(r: RawUITemplate): ServerUITemplate {\n const s = JSON.parse(r.settings_json) as Omit<ServerUITemplate, 'id' | 'tenantId' | 'name' | 'createdAt' | 'updatedAt'>\n return { id: r.id, tenantId: r.tenant_id, name: r.name, ...s, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n this.stmt('INSERT INTO ui_templates (id,tenant_id,name,settings_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run(id, tenantId, name, JSON.stringify(settings), now, now)\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n if (!row) throw new Error('UI template not found')\n if (data.name !== undefined) this.stmt('UPDATE ui_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n const { name: _n, ...settingsUpdate } = data\n if (Object.keys(settingsUpdate).length > 0) {\n const current = JSON.parse(row.settings_json) as Record<string, unknown>\n const merged = { ...current, ...settingsUpdate }\n this.stmt('UPDATE ui_templates SET settings_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(merged), now, id)\n }\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM ui_templates WHERE id = ?').run(id)\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n return row ? this.mapUITemplate(row) : null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n const rows = this.stmt('SELECT * FROM ui_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawUITemplate[]\n return rows.map(r => this.mapUITemplate(r))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src\n return this.createUITemplate({ tenantId, name: newName, mainBanner, gpcBanner, preferenceModal })\n }\n}\n","import type { MongoClient, Db } from 'mongodb'\nimport { randomUUID } from '../../utils/crypto'\nimport { SEED_TENANT, SEED_PERMISSIONS, SEED_ROLES, SEED_ROLE_PERMISSIONS } from '../seed-data'\nimport type {\n StorageAdapter, StorageConfig, Profile, ProfileConfig,\n CreateProfileInput, UpdateProfileInput,\n ConsentDbRecord, ConsentHistoryEntry, ConsentValue, ConsentFilters,\n CreateConsentInput, UpdateConsentInput,\n Visitor, VisitorFilters, CreateVisitorInput, UpdateVisitorInput,\n AdminUser, CreateUserInput, UpdateUserInput,\n Role, CreateRoleInput, UpdateRoleInput,\n Permission, AuditLog, AuditFilters, CreateAuditLogInput,\n OverviewStats, CategoryStats, TimelineEntry,\n CountryStat, GpcStats, Tenant, ApiKey, CreateApiKeyInput,\n CreateTenantInput, UpdateTenantInput,\n} from '@consenti/types'\n\n// ── Raw document shapes ────────────────────────────────────────────────────────\n\ninterface DocProfile {\n _id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: ProfileConfig\n created_at: string; updated_at: string\n}\ninterface DocConsent {\n _id: string; tenant_id: string; visitor_id: string; profile_id: string\n profile_version: number; locale: string; consent_json: ConsentValue\n gpc_detected: boolean; source: string; created_at: string; updated_at: string\n}\ninterface DocConsentHistory {\n _id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: ConsentValue | null; new_json: ConsentValue; action: string; created_at: string\n}\ninterface DocVisitor {\n _id: string; tenant_id: string; visitor_id: string\n country?: string; region?: string; city?: string\n ip_hash?: string; user_agent_hash?: string\n first_seen: string; last_seen: string\n}\ninterface DocUser {\n _id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: boolean; created_at: string; updated_at: string\n}\ninterface DocRole { _id: string; tenant_id: string; name: string; description?: string }\ninterface DocPermission { _id: string; name: string; description?: string }\ninterface DocUserRole { user_id: string; role_id: string; tenant_id?: string }\ninterface DocRolePermission { role_id: string; permission_id: string }\ninterface DocAuditLog {\n _id: string; tenant_id: string; user_id?: string; action: string\n resource_type: string; resource_id?: string\n old_data?: unknown; new_data?: unknown; created_at: string\n}\ninterface DocApiKey {\n _id: string; tenant_id: string; key_hash: string; name: string; is_active: boolean; created_at: string\n}\n\n// ── Mapper helpers ─────────────────────────────────────────────────────────────\n\nfunction cast<T extends object>(doc: object | null): T | null {\n return doc as T | null\n}\n\nfunction castArr<T extends object>(docs: object[]): T[] {\n return docs as T[]\n}\n\nfunction mapProfile(d: DocProfile): Profile {\n return {\n id: d._id, tenantId: d.tenant_id, name: d.name,\n defaultLocale: d.default_locale, version: d.version,\n profileJson: d.profile_json, createdAt: d.created_at, updatedAt: d.updated_at,\n }\n}\n\nfunction mapConsent(d: DocConsent): ConsentDbRecord {\n return {\n id: d._id, tenantId: d.tenant_id, visitorId: d.visitor_id, profileId: d.profile_id,\n profileVersion: d.profile_version, locale: d.locale, consentJson: d.consent_json,\n gpcDetected: d.gpc_detected, source: d.source as ConsentDbRecord['source'],\n createdAt: d.created_at, updatedAt: d.updated_at,\n }\n}\n\nfunction mapHistory(d: DocConsentHistory): ConsentHistoryEntry {\n return {\n id: d._id, tenantId: d.tenant_id, consentRecordId: d.consent_record_id,\n visitorId: d.visitor_id, oldJson: d.old_json, newJson: d.new_json,\n action: d.action as ConsentHistoryEntry['action'], createdAt: d.created_at,\n }\n}\n\nfunction mapVisitor(d: DocVisitor): Visitor {\n return {\n id: d._id, tenantId: d.tenant_id, visitorId: d.visitor_id,\n ...(d.country != null ? { country: d.country } : {}),\n ...(d.region != null ? { region: d.region } : {}),\n ...(d.city != null ? { city: d.city } : {}),\n ...(d.ip_hash != null ? { ipHash: d.ip_hash } : {}),\n ...(d.user_agent_hash!= null ? { userAgentHash: d.user_agent_hash} : {}),\n firstSeen: d.first_seen, lastSeen: d.last_seen,\n }\n}\n\nfunction mapUser(d: DocUser): AdminUser {\n return {\n id: d._id, tenantId: d.tenant_id, name: d.name, email: d.email,\n passwordHash: d.password_hash, isActive: d.is_active,\n createdAt: d.created_at, updatedAt: d.updated_at,\n }\n}\n\nfunction mapRole(d: DocRole): Role {\n return {\n id: d._id, tenantId: d.tenant_id, name: d.name,\n ...(d.description != null ? { description: d.description } : {}),\n }\n}\n\nfunction mapPermission(d: DocPermission): Permission {\n return {\n id: d._id, name: d.name,\n ...(d.description != null ? { description: d.description } : {}),\n }\n}\n\nfunction mapAuditLog(d: DocAuditLog): AuditLog {\n return {\n id: d._id, tenantId: d.tenant_id, action: d.action, resourceType: d.resource_type,\n ...(d.user_id != null ? { userId: d.user_id } : {}),\n ...(d.resource_id != null ? { resourceId: d.resource_id } : {}),\n ...(d.old_data != null ? { oldData: d.old_data } : {}),\n ...(d.new_data != null ? { newData: d.new_data } : {}),\n createdAt: d.created_at,\n }\n}\n\n// ── Adapter ────────────────────────────────────────────────────────────────────\n\n// Custom thin wrappers so MongoDB's ObjectId generics don't interfere with our string _id.\n// DocCursor extends AsyncIterable so it supports `for await...of` iteration.\ninterface DocCursor extends AsyncIterable<object> {\n sort(s: object): DocCursor\n skip(n: number): DocCursor\n limit(n: number): DocCursor\n toArray(): Promise<object[]>\n}\ninterface DocCollection {\n insertOne(doc: object): Promise<object>\n findOne(filter: object): Promise<object | null>\n findOneAndUpdate(filter: object, update: object, options?: object): Promise<object | null>\n find(filter: object): DocCursor\n updateOne(filter: object, update: object, options?: object): Promise<object>\n deleteOne(filter: object): Promise<object>\n deleteMany(filter: object): Promise<object>\n countDocuments(filter?: object): Promise<number>\n createIndex(spec: object, options?: object): Promise<object>\n aggregate(pipeline: object[]): { toArray(): Promise<object[]> }\n}\n\nexport class MongoDBAdapter implements StorageAdapter {\n private client!: MongoClient\n private db!: Db\n\n constructor(private config: StorageConfig) {}\n\n private col(name: string): DocCollection {\n // MongoDB's Collection generics assume ObjectId for _id; we use string UUIDs throughout.\n // The cast via unknown is the TypeScript-sanctioned way to bridge structurally incompatible types.\n return this.db.collection(name) as unknown as DocCollection\n }\n\n async connect(): Promise<void> {\n const { MongoClient: MC } = (await import('mongodb')) as { MongoClient: typeof MongoClient }\n const uri = this.config.uri ?? 'mongodb://localhost:27017'\n this.client = new MC(uri)\n await this.client.connect()\n this.db = this.client.db(this.config.dbName ?? this.config.database ?? 'consenti')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.client.close()\n }\n\n async migrate(): Promise<void> {\n await this.createIndexes()\n await this.seed()\n }\n\n private async createIndexes(): Promise<void> {\n await this.col('consent_records').createIndex({ visitor_id: 1, profile_id: 1 }, { unique: true })\n await this.col('consent_records').createIndex({ tenant_id: 1 })\n await this.col('consent_records').createIndex({ created_at: 1 })\n await this.col('visitors').createIndex({ visitor_id: 1 }, { unique: true })\n await this.col('visitors').createIndex({ tenant_id: 1 })\n await this.col('users').createIndex({ email: 1 }, { unique: true })\n await this.col('audit_logs').createIndex({ tenant_id: 1, created_at: 1 })\n await this.col('user_roles').createIndex({ user_id: 1 })\n await this.col('role_permissions').createIndex({ role_id: 1 })\n }\n\n private async seed(): Promise<void> {\n const { id, name, slug } = SEED_TENANT\n if (!(await this.col('tenants').findOne({ _id: id }))) {\n await this.col('tenants').insertOne({ _id: id, name, slug })\n }\n for (const p of SEED_PERMISSIONS) {\n const doc = { _id: p.id, name: p.name, description: p.description }\n await this.col('permissions').updateOne({ _id: p.id }, { $setOnInsert: doc }, { upsert: true })\n }\n for (const r of SEED_ROLES) {\n const doc = { _id: r.id, tenant_id: r.tenantId, name: r.name, description: r.description }\n await this.col('roles').updateOne({ _id: r.id }, { $setOnInsert: doc }, { upsert: true })\n }\n const rp = this.col('role_permissions')\n for (const { roleId, permissionId } of SEED_ROLE_PERMISSIONS) {\n await rp.updateOne(\n { role_id: roleId, permission_id: permissionId },\n { $setOnInsert: { role_id: roleId, permission_id: permissionId } },\n { upsert: true },\n )\n }\n }\n\n // ── Profiles ─────────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const now = new Date().toISOString()\n const doc: DocProfile = {\n _id: randomUUID(), tenant_id: data.tenantId, name: data.name,\n default_locale: data.defaultLocale, version: 1,\n profile_json: data.profileJson,\n created_at: now, updated_at: now,\n }\n await this.col('profiles').insertOne(doc)\n return mapProfile(doc)\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const now = new Date().toISOString()\n const set: Record<string, unknown> = { updated_at: now }\n if (data.name != null) set['name'] = data.name\n if (data.defaultLocale != null) set['default_locale'] = data.defaultLocale\n if (data.profileJson != null) set['profile_json'] = data.profileJson\n const res = cast<DocProfile>(await this.col('profiles').findOneAndUpdate(\n { _id: id }, { $set: set, $inc: { version: 1 } }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Profile ${id} not found`)\n return mapProfile(res)\n }\n\n async deleteProfile(id: string): Promise<void> {\n await this.col('profiles').deleteOne({ _id: id })\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const doc = cast<DocProfile>(await this.col('profiles').findOne({ _id: id }))\n return doc ? mapProfile(doc) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const docs = castArr<DocProfile>(await this.col('profiles').find({ tenant_id: tenantId }).toArray())\n return docs.map(mapProfile)\n }\n\n // ── Consents ─────────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const now = new Date().toISOString()\n const doc: DocConsent = {\n _id: randomUUID(), tenant_id: data.tenantId, visitor_id: data.visitorId,\n profile_id: data.profileId, profile_version: data.profileVersion,\n locale: data.locale, consent_json: data.consentJson,\n gpc_detected: data.gpcDetected, source: data.source,\n created_at: now, updated_at: now,\n }\n await this.col('consent_records').insertOne(doc)\n await this.writeHistory(doc._id, data.tenantId, data.visitorId, null, data.consentJson, 'created')\n return mapConsent(doc)\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const set: Record<string, unknown> = { updated_at: now, consent_json: data.consentJson }\n if (data.locale != null) set['locale'] = data.locale\n if (data.gpcDetected != null) set['gpc_detected'] = data.gpcDetected\n const res = cast<DocConsent>(await this.col('consent_records').findOneAndUpdate(\n { visitor_id: visitorId }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Consent for visitor ${visitorId} not found`)\n await this.writeHistory(res._id, res.tenant_id, visitorId, existing.consentJson, data.consentJson, 'updated')\n return mapConsent(res)\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n await this.col('consent_history').deleteMany({ visitor_id: visitorId })\n await this.col('consent_records').deleteOne({ visitor_id: visitorId })\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const doc = cast<DocConsent>(await this.col('consent_records').findOne({ visitor_id: visitorId }))\n return doc ? mapConsent(doc) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n const q = this.buildConsentFilter(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const docs = castArr<DocConsent>(\n await this.col('consent_records').find(q).sort({ created_at: -1 }).skip((page - 1) * limit).limit(limit).toArray(),\n )\n return docs.map(mapConsent)\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const q = this.buildConsentFilter(filters)\n const cursor = this.col('consent_records').find(q).sort({ created_at: 1 })\n for await (const doc of cursor) yield mapConsent(doc as unknown as DocConsent)\n }\n\n private buildConsentFilter(f: ConsentFilters): Record<string, unknown> {\n const q: Record<string, unknown> = { tenant_id: f.tenantId }\n if (f.profileId != null) q['profile_id'] = f.profileId\n if (f.from != null || f.to != null) {\n const range: Record<string, string> = {}\n if (f.from != null) range['$gte'] = f.from\n if (f.to != null) range['$lte'] = f.to\n q['created_at'] = range\n }\n return q\n }\n\n private async writeHistory(\n consentRecordId: string, tenantId: string, visitorId: string,\n oldJson: ConsentValue | null, newJson: ConsentValue, action: string,\n ): Promise<void> {\n const doc: DocConsentHistory = {\n _id: randomUUID(), tenant_id: tenantId, consent_record_id: consentRecordId,\n visitor_id: visitorId, old_json: oldJson, new_json: newJson,\n action, created_at: new Date().toISOString(),\n }\n await this.col('consent_history').insertOne(doc)\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const docs = castArr<DocConsentHistory>(\n await this.col('consent_history').find({ visitor_id: visitorId }).sort({ created_at: 1 }).toArray(),\n )\n return docs.map(mapHistory)\n }\n\n // ── Visitors ─────────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const doc: DocVisitor = {\n _id: randomUUID(), tenant_id: data.tenantId, visitor_id: data.visitorId,\n first_seen: now, last_seen: now,\n ...(data.country != null ? { country: data.country } : {}),\n ...(data.region != null ? { region: data.region } : {}),\n ...(data.city != null ? { city: data.city } : {}),\n ...(data.ipHash != null ? { ip_hash: data.ipHash } : {}),\n ...(data.userAgentHash != null ? { user_agent_hash: data.userAgentHash } : {}),\n }\n await this.col('visitors').insertOne(doc)\n return mapVisitor(doc)\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const set: Record<string, string> = { last_seen: data.lastSeen ?? new Date().toISOString() }\n if (data.country != null) set['country'] = data.country\n if (data.region != null) set['region'] = data.region\n if (data.city != null) set['city'] = data.city\n const res = cast<DocVisitor>(await this.col('visitors').findOneAndUpdate(\n { visitor_id: visitorId }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Visitor ${visitorId} not found`)\n return mapVisitor(res)\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n await this.col('visitors').deleteOne({ visitor_id: visitorId })\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const doc = cast<DocVisitor>(await this.col('visitors').findOne({ visitor_id: visitorId }))\n return doc ? mapVisitor(doc) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n const q: Record<string, unknown> = { tenant_id: filters.tenantId }\n if (filters.from != null || filters.to != null) {\n const r: Record<string, string> = {}\n if (filters.from != null) r['$gte'] = filters.from\n if (filters.to != null) r['$lte'] = filters.to\n q['first_seen'] = r\n }\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const docs = castArr<DocVisitor>(\n await this.col('visitors').find(q).sort({ first_seen: -1 }).skip((page - 1) * limit).limit(limit).toArray(),\n )\n return docs.map(mapVisitor)\n }\n\n // ── Stats ─────────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const [total, gpcCount, visitors] = await Promise.all([\n this.col('consent_records').countDocuments({ tenant_id: tenantId }),\n this.col('consent_records').countDocuments({ tenant_id: tenantId, gpc_detected: true }),\n this.col('visitors').countDocuments({ tenant_id: tenantId }),\n ])\n\n const pipeline = [\n { $match: { tenant_id: tenantId } },\n { $addFields: { values: { $objectToArray: '$consent_json' } } },\n { $unwind: '$values' },\n { $group: { _id: '$values.v', count: { $sum: 1 } } },\n ]\n const results = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: string; count: number }>\n\n let granted = 0, denied = 0, all = 0\n for (const r of results) {\n all += r.count\n if (r._id === 'granted') granted = r.count\n if (r._id === 'denied') denied = r.count\n }\n return {\n totalConsents: total,\n acceptedPct: all > 0 ? Math.round((granted / all) * 100) : 0,\n rejectedPct: all > 0 ? Math.round((denied / all) * 100) : 0,\n totalVisitors: visitors,\n gpcDetectedCount: gpcCount,\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const pipeline = [\n { $match: { tenant_id: tenantId } },\n { $addFields: { pairs: { $objectToArray: '$consent_json' } } },\n { $unwind: '$pairs' },\n { $group: { _id: { cookie: '$pairs.k', status: '$pairs.v' }, count: { $sum: 1 } } },\n ]\n const rows = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: { cookie: string; status: string }; count: number }>\n const result: CategoryStats = {}\n for (const r of rows) {\n const cookie = r._id.cookie\n if (!result[cookie]) result[cookie] = { granted: 0, denied: 0, objected: 0 }\n const entry = result[cookie]\n if (entry) {\n if (r._id.status === 'granted') entry.granted += r.count\n if (r._id.status === 'denied') entry.denied += r.count\n if (r._id.status === 'objected') entry.objected += r.count\n }\n }\n return result\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const from = new Date(Date.now() - days * 86400_000).toISOString()\n const pipeline = [\n { $match: { tenant_id: tenantId, created_at: { $gte: from } } },\n { $group: { _id: { $substr: ['$created_at', 0, 10] }, count: { $sum: 1 } } },\n { $sort: { _id: 1 } },\n ]\n const rows = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: string; count: number }>\n return rows.map(r => ({ date: r._id, count: r.count }))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const doc: DocUser = {\n _id: randomUUID(), tenant_id: data.tenantId, name: data.name,\n email: data.email, password_hash: data.passwordHash,\n is_active: true, created_at: now, updated_at: now,\n }\n await this.col('users').insertOne(doc)\n return mapUser(doc)\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const set: Record<string, unknown> = { updated_at: new Date().toISOString() }\n if (data.name != null) set['name'] = data.name\n if (data.email != null) set['email'] = data.email\n if (data.passwordHash != null) set['password_hash'] = data.passwordHash\n if (data.isActive != null) set['is_active'] = data.isActive\n const res = cast<DocUser>(await this.col('users').findOneAndUpdate(\n { _id: id }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`User ${id} not found`)\n return mapUser(res)\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.col('user_roles').deleteMany({ user_id: id })\n await this.col('users').deleteOne({ _id: id })\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const doc = cast<DocUser>(await this.col('users').findOne({ _id: id }))\n return doc ? mapUser(doc) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const doc = cast<DocUser>(await this.col('users').findOne({ email }))\n return doc ? mapUser(doc) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const docs = castArr<DocUser>(await this.col('users').find({ tenant_id: tenantId }).toArray())\n return docs.map(mapUser)\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n return this.col('users').countDocuments(tenantId ? { tenant_id: tenantId } : {})\n }\n\n // ── Roles & permissions ───────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const docs = castArr<DocRole>(await this.col('roles').find({ tenant_id: tenantId }).toArray())\n return docs.map(mapRole)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const doc: DocRole = {\n _id: randomUUID(), tenant_id: data.tenantId, name: data.name,\n ...(data.description != null ? { description: data.description } : {}),\n }\n await this.col('roles').insertOne(doc)\n return mapRole(doc)\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const set: Record<string, string> = {}\n if (data.name != null) set['name'] = data.name\n if (data.description != null) set['description'] = data.description\n const res = cast<DocRole>(await this.col('roles').findOneAndUpdate(\n { _id: id }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Role ${id} not found`)\n return mapRole(res)\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.col('role_permissions').deleteMany({ role_id: id })\n await this.col('user_roles').deleteMany({ role_id: id })\n await this.col('roles').deleteOne({ _id: id })\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const docs = castArr<DocPermission>(await this.col('permissions').find({}).toArray())\n return docs.map(mapPermission)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rps = castArr<DocRolePermission>(\n await this.col('role_permissions').find({ role_id: roleId }).toArray(),\n )\n const ids = rps.map(r => r.permission_id)\n if (ids.length === 0) return []\n const docs = castArr<DocPermission>(await this.col('permissions').find({ _id: { $in: ids } }).toArray())\n return docs.map(mapPermission)\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const filter = tenantId\n ? { user_id: userId, tenant_id: tenantId }\n : { user_id: userId }\n const urs = castArr<DocUserRole>(await this.col('user_roles').find(filter).toArray())\n const ids = urs.map(r => r.role_id)\n if (ids.length === 0) return []\n const docs = castArr<DocRole>(await this.col('roles').find({ _id: { $in: ids } }).toArray())\n return docs.map(mapRole)\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n await this.col('user_roles').updateOne(\n { user_id: userId, role_id: roleId, tenant_id: tenantId },\n { $setOnInsert: { user_id: userId, role_id: roleId, tenant_id: tenantId } },\n { upsert: true },\n )\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n await this.col('user_roles').deleteOne({ user_id: userId, role_id: roleId, tenant_id: tenantId })\n } else {\n await this.col('user_roles').deleteMany({ user_id: userId, role_id: roleId })\n }\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n await this.col('role_permissions').updateOne(\n { role_id: roleId, permission_id: permissionId },\n { $setOnInsert: { role_id: roleId, permission_id: permissionId } },\n { upsert: true },\n )\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n await this.col('role_permissions').deleteOne({ role_id: roleId, permission_id: permissionId })\n }\n\n // ── Audit logs ────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const doc: DocAuditLog = {\n _id: randomUUID(), tenant_id: data.tenantId, action: data.action,\n resource_type: data.resourceType,\n ...(data.userId != null ? { user_id: data.userId } : {}),\n ...(data.resourceId != null ? { resource_id: data.resourceId } : {}),\n ...(data.oldData != null ? { old_data: data.oldData } : {}),\n ...(data.newData != null ? { new_data: data.newData } : {}),\n created_at: new Date().toISOString(),\n }\n await this.col('audit_logs').insertOne(doc)\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n const q = this.buildAuditFilter(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const docs = castArr<DocAuditLog>(\n await this.col('audit_logs').find(q).sort({ created_at: -1 }).skip((page - 1) * limit).limit(limit).toArray(),\n )\n return docs.map(mapAuditLog)\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const q = this.buildAuditFilter(filters)\n const cursor = this.col('audit_logs').find(q).sort({ created_at: 1 })\n for await (const doc of cursor) yield mapAuditLog(doc as unknown as DocAuditLog)\n }\n\n private buildAuditFilter(f: AuditFilters): Record<string, unknown> {\n const q: Record<string, unknown> = { tenant_id: f.tenantId }\n if (f.action != null) q['action'] = f.action\n if (f.resourceType != null) q['resource_type'] = f.resourceType\n if (f.from != null || f.to != null) {\n const r: Record<string, string> = {}\n if (f.from != null) r['$gte'] = f.from\n if (f.to != null) r['$lte'] = f.to\n q['created_at'] = r\n }\n return q\n }\n\n // ── Stats extensions ──────────────────────────────────────────────────────────\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const pipeline = [\n { $match: { tenant_id: tenantId } },\n { $lookup: { from: 'visitors', localField: 'visitor_id', foreignField: 'visitor_id', as: 'v' } },\n { $unwind: '$v' },\n { $match: { 'v.country': { $ne: null } } },\n { $group: { _id: '$v.country', count: { $sum: 1 } } },\n { $sort: { count: -1 } },\n { $limit: 50 },\n ]\n const rows = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: string; count: number }>\n return rows.map(r => ({ country: r._id, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = await this.col('consent_records').countDocuments({ tenant_id: tenantId })\n const detected = await this.col('consent_records').countDocuments({ tenant_id: tenantId, gpc_detected: true })\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ───────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const docs = castArr<{ _id: string; name: string; slug: string; created_at: string; updated_at: string }>(\n await this.col('tenants').find({}).toArray()\n )\n return docs.map(d => ({ id: d._id, name: d.name, slug: d.slug, createdAt: d.created_at, updatedAt: d.updated_at }))\n }\n\n // ── API Keys ──────────────────────────────────────────────────────────────────\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const doc: DocApiKey = {\n _id: randomUUID(), tenant_id: data.tenantId, key_hash: data.keyHash,\n name: data.name, is_active: true, created_at: new Date().toISOString(),\n }\n await this.col('api_keys').insertOne(doc)\n return { id: doc._id, tenantId: doc.tenant_id, keyHash: doc.key_hash, name: doc.name, isActive: doc.is_active, createdAt: doc.created_at }\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const doc = cast<DocApiKey>(await this.col('api_keys').findOne({ key_hash: keyHash, is_active: true }))\n if (!doc) return null\n return { id: doc._id, tenantId: doc.tenant_id, keyHash: doc.key_hash, name: doc.name, isActive: doc.is_active, createdAt: doc.created_at }\n }\n\n async revokeApiKey(id: string): Promise<void> {\n await this.col('api_keys').updateOne({ _id: id }, { $set: { is_active: false } })\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const docs = castArr<DocApiKey>(await this.col('api_keys').find({ tenant_id: tenantId }).sort({ created_at: -1 }).toArray())\n return docs.map(d => ({ id: d._id, tenantId: d.tenant_id, keyHash: d.key_hash, name: d.name, isActive: d.is_active, createdAt: d.created_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const doc = { _id: id, name: data.name, slug: data.slug, created_at: now, updated_at: now }\n await this.col('tenants').insertOne(doc)\n return { id, name: data.name, slug: data.slug, createdAt: now, updatedAt: now }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const update: Record<string, string> = { updated_at: now }\n if (data.name != null) update['name'] = data.name\n if (data.slug != null) update['slug'] = data.slug\n await this.col('tenants').updateOne({ _id: id }, { $set: update })\n const doc = cast<{ _id: string; name: string; slug: string; created_at: string; updated_at: string }>(\n await this.col('tenants').findOne({ _id: id })\n )\n if (!doc) throw new Error(`Tenant ${id} not found`)\n return { id: doc._id, name: doc.name, slug: doc.slug, createdAt: doc.created_at, updatedAt: doc.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n await this.col('tenants').deleteOne({ _id: id })\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const docs = castArr<{ visitor_id: string }>(\n await this.col('consent_records').find({ created_at: { $lt: cutoff } }).toArray()\n )\n if (docs.length === 0) return 0\n const visitorIds = docs.map(d => d.visitor_id)\n await this.col('consent_history').deleteMany({ visitor_id: { $in: visitorIds } })\n await this.col('consent_records').deleteMany({ visitor_id: { $in: visitorIds } })\n return docs.length\n }\n\n // Template methods — not yet implemented for MongoDB adapter\n async createCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteCookieTemplate(): Promise<void> { throw new Error('Not implemented') }\n async getCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async getCookieTemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async createUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteUITemplate(): Promise<void> { throw new Error('Not implemented') }\n async getUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async getUITemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyUITemplate(): Promise<never> { throw new Error('Not implemented') }\n}\n","import type { Pool, PoolConnection, RowDataPacket, ResultSetHeader } from 'mysql2/promise'\nimport { randomUUID } from '../../utils/crypto'\nimport { SEED_TENANT, SEED_PERMISSIONS, SEED_ROLES, SEED_ROLE_PERMISSIONS, SCHEMA_SQL_MYSQL } from '../seed-data'\nimport type {\n StorageAdapter, StorageConfig, Profile, ProfileConfig,\n CreateProfileInput, UpdateProfileInput,\n ConsentDbRecord, ConsentHistoryEntry, ConsentValue, ConsentFilters,\n CreateConsentInput, UpdateConsentInput,\n Visitor, VisitorFilters, CreateVisitorInput, UpdateVisitorInput,\n AdminUser, CreateUserInput, UpdateUserInput,\n Role, CreateRoleInput, UpdateRoleInput,\n Permission, AuditLog, AuditFilters, CreateAuditLogInput,\n OverviewStats, CategoryStats, TimelineEntry,\n CountryStat, GpcStats, Tenant, ApiKey, CreateApiKeyInput,\n CreateTenantInput, UpdateTenantInput,\n} from '@consenti/types'\n\n// ── Raw row shapes ─────────────────────────────────────────────────────────────\n\ninterface RowProfile extends RowDataPacket {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RowConsent extends RowDataPacket {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n profile_version: number; locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n}\ninterface RowConsentHistory extends RowDataPacket {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RowVisitor extends RowDataPacket {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RowUser extends RowDataPacket {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n}\ninterface RowRole extends RowDataPacket {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RowPermission extends RowDataPacket {\n id: string; name: string; description: string | null\n}\ninterface RowAuditLog extends RowDataPacket {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RowCount extends RowDataPacket { total: number }\ninterface RowStatus extends RowDataPacket { status: string; count: number }\ninterface RowDateCount extends RowDataPacket { date: string; count: number }\ninterface RowCookieStatus extends RowDataPacket { cookie_id: string; status: string; count: number }\ninterface RowCountry extends RowDataPacket { country: string; count: number }\ninterface RowApiKey extends RowDataPacket {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number; created_at: string\n}\ninterface RowTenant extends RowDataPacket { id: string; name: string; slug: string; created_at: string; updated_at: string }\n\n// ── Mappers ────────────────────────────────────────────────────────────────────\n\nfunction mapProfile(r: RowProfile): Profile {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n defaultLocale: r.default_locale, version: r.version,\n profileJson: JSON.parse(r.profile_json) as ProfileConfig,\n createdAt: r.created_at, updatedAt: r.updated_at,\n }\n}\n\nfunction mapConsent(r: RowConsent): ConsentDbRecord {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id,\n profileVersion: r.profile_version, locale: r.locale,\n consentJson: JSON.parse(r.consent_json) as ConsentValue,\n gpcDetected: r.gpc_detected === 1, source: r.source as ConsentDbRecord['source'],\n createdAt: r.created_at, updatedAt: r.updated_at,\n ...(r.age_verified != null ? { ageVerified: r.age_verified === 1 } : {}),\n ...(r.parental_consent_token != null ? { parentalConsentToken: r.parental_consent_token } : {}),\n ...(r.tcf_string != null ? { tcfString: r.tcf_string } : {}),\n }\n}\n\nfunction mapHistory(r: RowConsentHistory): ConsentHistoryEntry {\n return {\n id: r.id, tenantId: r.tenant_id, consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'], createdAt: r.created_at,\n }\n}\n\nfunction mapVisitor(r: RowVisitor): Visitor {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n ...(r.country != null ? { country: r.country } : {}),\n ...(r.region != null ? { region: r.region } : {}),\n ...(r.city != null ? { city: r.city } : {}),\n ...(r.ip_hash != null ? { ipHash: r.ip_hash } : {}),\n ...(r.user_agent_hash!= null ? { userAgentHash: r.user_agent_hash} : {}),\n firstSeen: r.first_seen, lastSeen: r.last_seen,\n }\n}\n\nfunction mapUser(r: RowUser): AdminUser {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name, email: r.email,\n passwordHash: r.password_hash, isActive: r.is_active === 1,\n createdAt: r.created_at, updatedAt: r.updated_at,\n totpEnabled: r.totp_enabled === 1,\n ...(r.totp_secret != null ? { totpSecret: r.totp_secret } : {}),\n }\n}\n\nfunction mapRole(r: RowRole): Role {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapPermission(r: RowPermission): Permission {\n return {\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapAuditLog(r: RowAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n createdAt: r.created_at,\n }\n}\n\n// ── Adapter ────────────────────────────────────────────────────────────────────\n\nexport class MySQLAdapter implements StorageAdapter {\n private pool!: Pool\n\n constructor(private config: StorageConfig) {}\n\n private async q<T extends RowDataPacket>(sql: string, params: (string | number | boolean | null)[] = []): Promise<T[]> {\n const [rows] = await this.pool.query<T[]>(sql, params)\n return rows\n }\n\n private async exec(sql: string, params: (string | number | boolean | null)[] = []): Promise<ResultSetHeader> {\n const [result] = await this.pool.query<ResultSetHeader>(sql, params)\n return result\n }\n\n async connect(): Promise<void> {\n const mysql = (await import('mysql2/promise')) as { createPool: typeof import('mysql2/promise').createPool }\n const cfg = this.config\n if (cfg.uri) {\n this.pool = mysql.createPool(cfg.uri)\n } else {\n this.pool = mysql.createPool({\n host: cfg.host ?? 'localhost',\n port: cfg.port ?? 3306,\n user: cfg.user ?? 'root',\n password: cfg.password ?? '',\n database: cfg.database ?? cfg.dbName ?? 'consenti',\n })\n }\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.pool.end()\n }\n\n async migrate(): Promise<void> {\n const conn: PoolConnection = await this.pool.getConnection()\n try {\n const [tables] = await conn.query<RowDataPacket[]>('SHOW TABLES LIKE ?', ['schema_version'])\n if (tables.length === 0) {\n for (const stmt of SCHEMA_SQL_MYSQL.split(';').map(s => s.trim()).filter(Boolean)) {\n await conn.query(stmt)\n }\n await this.seed(conn)\n await conn.query('INSERT IGNORE INTO schema_version (version) VALUES (2)')\n return\n }\n const [vrows] = await conn.query<RowDataPacket[]>('SELECT version FROM schema_version ORDER BY version DESC LIMIT 1')\n const version = (vrows[0] as { version?: number } | undefined)?.version ?? 0\n if (version < 2) {\n const alters = [\n 'ALTER TABLE consent_records ADD COLUMN IF NOT EXISTS age_verified TINYINT(1) NOT NULL DEFAULT 0',\n 'ALTER TABLE consent_records ADD COLUMN IF NOT EXISTS parental_consent_token TEXT',\n 'ALTER TABLE consent_records ADD COLUMN IF NOT EXISTS tcf_string TEXT',\n 'ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_secret TEXT',\n 'ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_enabled TINYINT(1) NOT NULL DEFAULT 0',\n ]\n for (const stmt of alters) await conn.query(stmt)\n await conn.query('INSERT IGNORE INTO schema_version (version) VALUES (2)')\n }\n if (version < 3) {\n await conn.query('ALTER TABLE user_roles ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(36) NOT NULL DEFAULT \\'default\\'')\n await conn.query('INSERT IGNORE INTO schema_version (version) VALUES (3)')\n }\n } finally {\n conn.release()\n }\n }\n\n private async seed(conn: PoolConnection): Promise<void> {\n await conn.query('INSERT IGNORE INTO tenants (id, name, slug) VALUES (?,?,?)', [SEED_TENANT.id, SEED_TENANT.name, SEED_TENANT.slug])\n for (const p of SEED_PERMISSIONS) {\n await conn.query('INSERT IGNORE INTO permissions (id, name, description) VALUES (?,?,?)', [p.id, p.name, p.description])\n }\n for (const r of SEED_ROLES) {\n await conn.query('INSERT IGNORE INTO roles (id, tenant_id, name, description) VALUES (?,?,?,?)', [r.id, r.tenantId, r.name, r.description])\n }\n for (const { roleId, permissionId } of SEED_ROLE_PERMISSIONS) {\n await conn.query('INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?,?)', [roleId, permissionId])\n }\n }\n\n // ── Profiles ─────────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO profiles (id, tenant_id, name, default_locale, profile_json) VALUES (?,?,?,?,?)',\n [id, data.tenantId, data.name, data.defaultLocale, JSON.stringify(data.profileJson)],\n )\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Profile creation failed')\n return mapProfile(row)\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const sets: string[] = ['version = version + 1']\n const vals: (string | number | boolean | null)[] = []\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.defaultLocale != null){ sets.push('default_locale=?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { sets.push('profile_json=?'); vals.push(JSON.stringify(data.profileJson)) }\n vals.push(id)\n await this.exec(`UPDATE profiles SET ${sets.join(',')} WHERE id=?`, vals)\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error(`Profile ${id} not found`)\n return mapProfile(row)\n }\n\n async deleteProfile(id: string): Promise<void> {\n await this.exec('DELETE FROM profiles WHERE id=?', [id])\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=?', [id])\n return rows[0] ? mapProfile(rows[0]) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE tenant_id=? ORDER BY created_at DESC', [tenantId])\n return rows.map(mapProfile)\n }\n\n // ── Consents ─────────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO consent_records (id, tenant_id, visitor_id, profile_id, profile_version, locale, consent_json, gpc_detected, source) VALUES (?,?,?,?,?,?,?,?,?)',\n [id, data.tenantId, data.visitorId, data.profileId, data.profileVersion, data.locale,\n JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source],\n )\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Consent creation failed')\n await this.writeHistory(id, data.tenantId, data.visitorId, null, data.consentJson, 'created')\n return mapConsent(row)\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const sets: string[] = ['consent_json=?']\n const vals: (string | number | boolean | null)[] = [JSON.stringify(data.consentJson)]\n if (data.locale != null) { sets.push('locale=?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { sets.push('gpc_detected=?'); vals.push(data.gpcDetected ? 1 : 0) }\n vals.push(visitorId)\n await this.exec(`UPDATE consent_records SET ${sets.join(',')} WHERE visitor_id=?`, vals)\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE visitor_id=?', [visitorId])\n const row = rows[0]\n if (!row) throw new Error(`Consent for visitor ${visitorId} not found`)\n await this.writeHistory(row.id, row.tenant_id, visitorId, existing.consentJson, data.consentJson, 'updated')\n return mapConsent(row)\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n await this.exec('DELETE FROM consent_history WHERE visitor_id=?', [visitorId])\n await this.exec('DELETE FROM consent_records WHERE visitor_id=?', [visitorId])\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE visitor_id=?', [visitorId])\n return rows[0] ? mapConsent(rows[0]) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n const { sql, params } = this.buildConsentQuery(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const rows = await this.q<RowConsent>(`${sql} ORDER BY created_at DESC LIMIT ? OFFSET ?`, [...params, limit, (page - 1) * limit])\n return rows.map(mapConsent)\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const { sql, params } = this.buildConsentQuery(filters)\n const rows = await this.q<RowConsent>(`${sql} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapConsent(r)\n }\n\n private buildConsentQuery(f: ConsentFilters): { sql: string; params: (string | number | boolean | null)[] } {\n let sql = 'SELECT * FROM consent_records WHERE tenant_id=?'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n if (f.profileId != null) { sql += ' AND profile_id=?'; params.push(f.profileId) }\n if (f.from != null) { sql += ' AND created_at>=?'; params.push(f.from) }\n if (f.to != null) { sql += ' AND created_at<=?'; params.push(f.to) }\n return { sql, params }\n }\n\n private async writeHistory(\n consentRecordId: string, tenantId: string, visitorId: string,\n oldJson: ConsentValue | null, newJson: ConsentValue, action: string,\n ): Promise<void> {\n await this.exec(\n 'INSERT INTO consent_history (id, tenant_id, consent_record_id, visitor_id, old_json, new_json, action) VALUES (?,?,?,?,?,?,?)',\n [randomUUID(), tenantId, consentRecordId, visitorId,\n oldJson ? JSON.stringify(oldJson) : null, JSON.stringify(newJson), action],\n )\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = await this.q<RowConsentHistory>('SELECT * FROM consent_history WHERE visitor_id=? ORDER BY created_at ASC', [visitorId])\n return rows.map(mapHistory)\n }\n\n // ── Visitors ─────────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO visitors (id, tenant_id, visitor_id, country, region, city, ip_hash, user_agent_hash) VALUES (?,?,?,?,?,?,?,?)',\n [id, data.tenantId, data.visitorId,\n data.country ?? null, data.region ?? null, data.city ?? null,\n data.ipHash ?? null, data.userAgentHash ?? null],\n )\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Visitor creation failed')\n return mapVisitor(row)\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const sets: string[] = ['last_seen=NOW()']\n const vals: (string | number | boolean | null)[] = []\n if (data.country != null) { sets.push('country=?'); vals.push(data.country) }\n if (data.region != null) { sets.push('region=?'); vals.push(data.region) }\n if (data.city != null) { sets.push('city=?'); vals.push(data.city) }\n vals.push(visitorId)\n await this.exec(`UPDATE visitors SET ${sets.join(',')} WHERE visitor_id=?`, vals)\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE visitor_id=?', [visitorId])\n const row = rows[0]\n if (!row) throw new Error(`Visitor ${visitorId} not found`)\n return mapVisitor(row)\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n await this.exec('DELETE FROM visitors WHERE visitor_id=?', [visitorId])\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE visitor_id=?', [visitorId])\n return rows[0] ? mapVisitor(rows[0]) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n let sql = 'SELECT * FROM visitors WHERE tenant_id=?'\n const params: (string | number | boolean | null)[] = [filters.tenantId]\n if (filters.from != null) { sql += ' AND first_seen>=?'; params.push(filters.from) }\n if (filters.to != null) { sql += ' AND first_seen<=?'; params.push(filters.to) }\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const rows = await this.q<RowVisitor>(`${sql} ORDER BY first_seen DESC LIMIT ? OFFSET ?`, [...params, limit, (page - 1) * limit])\n return rows.map(mapVisitor)\n }\n\n // ── Stats ─────────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const [[countRow], [gpcRow], [visitorRow]] = await Promise.all([\n this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=?', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=? AND gpc_detected=1', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*) AS total FROM visitors WHERE tenant_id=?', [tenantId]),\n ])\n const total = countRow?.total ?? 0\n const gpcCount = gpcRow?.total ?? 0\n const visitors = visitorRow?.total ?? 0\n\n const statusRows = await this.q<RowStatus>(\n `SELECT jt.status, COUNT(*) AS count\n FROM consent_records cr\n JOIN JSON_TABLE(cr.consent_json, '$.*' COLUMNS (status VARCHAR(20) PATH '$')) AS jt\n WHERE cr.tenant_id=?\n GROUP BY jt.status`,\n [tenantId],\n )\n let granted = 0, denied = 0, all = 0\n for (const r of statusRows) {\n all += r.count\n if (r.status === 'granted') granted = r.count\n if (r.status === 'denied') denied = r.count\n }\n return {\n totalConsents: total,\n acceptedPct: all > 0 ? Math.round((granted / all) * 100) : 0,\n rejectedPct: all > 0 ? Math.round((denied / all) * 100) : 0,\n totalVisitors: visitors,\n gpcDetectedCount: gpcCount,\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = await this.q<RowCookieStatus>(\n `SELECT jt.cookie_id, jt.status, COUNT(*) AS count\n FROM consent_records cr\n JOIN JSON_TABLE(cr.consent_json, '$.*' COLUMNS (NESTED PATH '$' COLUMNS (\n cookie_id VARCHAR(100) PATH '$.k',\n status VARCHAR(20) PATH '$.v'\n ))) AS jt\n WHERE cr.tenant_id=?\n GROUP BY jt.cookie_id, jt.status`,\n [tenantId],\n )\n const result: CategoryStats = {}\n for (const r of rows) {\n if (!result[r.cookie_id]) result[r.cookie_id] = { granted: 0, denied: 0, objected: 0 }\n const entry = result[r.cookie_id]\n if (entry) {\n if (r.status === 'granted') entry.granted += r.count\n if (r.status === 'denied') entry.denied += r.count\n if (r.status === 'objected') entry.objected += r.count\n }\n }\n return result\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const from = new Date(Date.now() - days * 86400_000).toISOString().slice(0, 10)\n const rows = await this.q<RowDateCount>(\n `SELECT DATE(created_at) AS date, COUNT(*) AS count\n FROM consent_records WHERE tenant_id=? AND created_at>=?\n GROUP BY DATE(created_at) ORDER BY date ASC`,\n [tenantId, from],\n )\n return rows.map(r => ({ date: String(r.date), count: r.count }))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO users (id, tenant_id, name, email, password_hash) VALUES (?,?,?,?,?)',\n [id, data.tenantId, data.name, data.email, data.passwordHash],\n )\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('User creation failed')\n return mapUser(row)\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.email != null) { sets.push('email=?'); vals.push(data.email) }\n if (data.passwordHash != null) { sets.push('password_hash=?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { sets.push('is_active=?'); vals.push(data.isActive ? 1 : 0) }\n if (sets.length > 0) { vals.push(id); await this.exec(`UPDATE users SET ${sets.join(',')} WHERE id=?`, vals) }\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error(`User ${id} not found`)\n return mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.exec('DELETE FROM user_roles WHERE user_id=?', [id])\n await this.exec('DELETE FROM users WHERE id=?', [id])\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=?', [id])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE email=?', [email])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE tenant_id=? ORDER BY created_at DESC', [tenantId])\n return rows.map(mapUser)\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const rows = tenantId\n ? await this.q<RowCount>('SELECT COUNT(*) AS total FROM users WHERE tenant_id=?', [tenantId])\n : await this.q<RowCount>('SELECT COUNT(*) AS total FROM users')\n return rows[0]?.total ?? 0\n }\n\n // ── Roles & permissions ───────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE tenant_id=?', [tenantId])\n return rows.map(mapRole)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO roles (id, tenant_id, name, description) VALUES (?,?,?,?)',\n [id, data.tenantId, data.name, data.description ?? null],\n )\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Role creation failed')\n return mapRole(row)\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.description != null) { sets.push('description=?'); vals.push(data.description) }\n if (sets.length > 0) { vals.push(id); await this.exec(`UPDATE roles SET ${sets.join(',')} WHERE id=?`, vals) }\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error(`Role ${id} not found`)\n return mapRole(row)\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.exec('DELETE FROM role_permissions WHERE role_id=?', [id])\n await this.exec('DELETE FROM user_roles WHERE role_id=?', [id])\n await this.exec('DELETE FROM roles WHERE id=?', [id])\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = await this.q<RowPermission>('SELECT * FROM permissions ORDER BY name')\n return rows.map(mapPermission)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = await this.q<RowPermission>(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON p.id=rp.permission_id WHERE rp.role_id=?',\n [roleId],\n )\n return rows.map(mapPermission)\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=? AND ur.tenant_id=?',\n [userId, tenantId],\n )\n : await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=?',\n [userId],\n )\n return rows.map(mapRole)\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n await this.exec('INSERT IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)', [userId, roleId, tenantId])\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n await this.exec('DELETE FROM user_roles WHERE user_id=? AND role_id=? AND tenant_id=?', [userId, roleId, tenantId])\n } else {\n await this.exec('DELETE FROM user_roles WHERE user_id=? AND role_id=?', [userId, roleId])\n }\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n await this.exec('INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?,?)', [roleId, permissionId])\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n await this.exec('DELETE FROM role_permissions WHERE role_id=? AND permission_id=?', [roleId, permissionId])\n }\n\n // ── Audit logs ────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n await this.exec(\n 'INSERT INTO audit_logs (id, tenant_id, user_id, action, resource_type, resource_id, old_data, new_data) VALUES (?,?,?,?,?,?,?,?)',\n [randomUUID(), data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null],\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n const { sql, params } = this.buildAuditQuery(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const rows = await this.q<RowAuditLog>(`${sql} ORDER BY created_at DESC LIMIT ? OFFSET ?`, [...params, limit, (page - 1) * limit])\n return rows.map(mapAuditLog)\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const { sql, params } = this.buildAuditQuery(filters)\n const rows = await this.q<RowAuditLog>(`${sql} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapAuditLog(r)\n }\n\n private buildAuditQuery(f: AuditFilters): { sql: string; params: (string | number | boolean | null)[] } {\n let sql = 'SELECT * FROM audit_logs WHERE tenant_id=?'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n if (f.action != null) { sql += ' AND action=?'; params.push(f.action) }\n if (f.resourceType != null) { sql += ' AND resource_type=?'; params.push(f.resourceType) }\n if (f.from != null) { sql += ' AND created_at>=?'; params.push(f.from) }\n if (f.to != null) { sql += ' AND created_at<=?'; params.push(f.to) }\n return { sql, params }\n }\n\n // ── Stats extensions ───────────────────────────────────────────────────────────\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = await this.q<RowCountry>(\n `SELECT v.country, COUNT(*) AS count FROM consent_records cr\n JOIN visitors v ON v.visitor_id=cr.visitor_id\n WHERE cr.tenant_id=? AND v.country IS NOT NULL\n GROUP BY v.country ORDER BY count DESC LIMIT 50`,\n [tenantId],\n )\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const [t] = await this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=?', [tenantId])\n const [d] = await this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=? AND gpc_detected=1', [tenantId])\n const total = t?.total ?? 0\n const detected = d?.total ?? 0\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ───────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = await this.q<RowTenant>('SELECT * FROM tenants ORDER BY name ASC', [])\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n // ── API Keys ──────────────────────────────────────────────────────────────────\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO api_keys (id, tenant_id, key_hash, name) VALUES (?,?,?,?)',\n [id, data.tenantId, data.keyHash, data.name],\n )\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE id=?', [id])\n const r = rows[0]\n if (!r) throw new Error('Failed to create API key')\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1, createdAt: r.created_at }\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE key_hash=? AND is_active=1', [keyHash])\n const r = rows[0]\n if (!r) return null\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1, createdAt: r.created_at }\n }\n\n async revokeApiKey(id: string): Promise<void> {\n await this.exec('UPDATE api_keys SET is_active=0 WHERE id=?', [id])\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE tenant_id=? ORDER BY created_at DESC', [tenantId])\n return rows.map(r => ({ id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1, createdAt: r.created_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n await this.exec('INSERT INTO tenants (id, name, slug, created_at, updated_at) VALUES (?,?,?,?,?)', [id, data.name, data.slug, now, now])\n const rows = await this.q<RowTenant>('SELECT * FROM tenants WHERE id=?', [id])\n const r = rows[0]\n if (!r) throw new Error('Failed to create tenant')\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const sets: string[] = ['updated_at=?']\n const vals: (string | number)[] = [now]\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.slug != null) { sets.push('slug=?'); vals.push(data.slug) }\n vals.push(id)\n await this.exec(`UPDATE tenants SET ${sets.join(',')} WHERE id=?`, vals)\n const rows = await this.q<RowTenant>('SELECT * FROM tenants WHERE id=?', [id])\n const r = rows[0]\n if (!r) throw new Error(`Tenant ${id} not found`)\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n await this.exec('DELETE FROM tenants WHERE id=?', [id])\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const visitorRows = await this.q<{ visitor_id: string } & RowDataPacket>(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?', [cutoff]\n )\n if (visitorRows.length === 0) return 0\n const ids = visitorRows.map(r => r.visitor_id)\n const placeholders = ids.map(() => '?').join(',')\n await this.exec(`DELETE FROM consent_history WHERE visitor_id IN (${placeholders})`, ids)\n await this.exec(`DELETE FROM consent_records WHERE visitor_id IN (${placeholders})`, ids)\n return ids.length\n }\n\n // Template methods — not yet implemented for MySQL adapter\n async createCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteCookieTemplate(): Promise<void> { throw new Error('Not implemented') }\n async getCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async getCookieTemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async createUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteUITemplate(): Promise<void> { throw new Error('Not implemented') }\n async getUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async getUITemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyUITemplate(): Promise<never> { throw new Error('Not implemented') }\n}\n","import type { Pool, PoolClient, QueryResult } from 'pg'\nimport { randomUUID } from '../../utils/crypto'\nimport { SEED_TENANT, SEED_PERMISSIONS, SEED_ROLES, SEED_ROLE_PERMISSIONS, SCHEMA_SQL_POSTGRES } from '../seed-data'\nimport type {\n StorageAdapter, StorageConfig, Profile, ProfileConfig,\n CreateProfileInput, UpdateProfileInput,\n ConsentDbRecord, ConsentHistoryEntry, ConsentValue, ConsentFilters,\n CreateConsentInput, UpdateConsentInput,\n Visitor, VisitorFilters, CreateVisitorInput, UpdateVisitorInput,\n AdminUser, CreateUserInput, UpdateUserInput,\n Role, CreateRoleInput, UpdateRoleInput,\n Permission, AuditLog, AuditFilters, CreateAuditLogInput,\n OverviewStats, CategoryStats, TimelineEntry,\n CountryStat, GpcStats, Tenant, ApiKey, CreateApiKeyInput,\n CreateTenantInput, UpdateTenantInput,\n} from '@consenti/types'\n\n// ── Raw row shapes ─────────────────────────────────────────────────────────────\n\ninterface RowProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RowConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n profile_version: number; locale: string; consent_json: string\n gpc_detected: boolean; source: string; created_at: string; updated_at: string\n age_verified?: boolean; parental_consent_token?: string | null; tcf_string?: string | null\n}\ninterface RowConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RowVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RowUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: boolean; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: boolean\n}\ninterface RowRole { id: string; tenant_id: string; name: string; description: string | null }\ninterface RowPermission { id: string; name: string; description: string | null }\ninterface RowAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RowCount { total: string }\ninterface RowStatus { status: string; count: string }\ninterface RowDateCount { date: string; count: string }\ninterface RowCookieStatus { cookie_id: string; status: string; count: string }\ninterface RowCountry { country: string; count: string }\ninterface RowApiKey { id: string; tenant_id: string; key_hash: string; name: string; is_active: boolean; created_at: string }\ninterface RowTenant { id: string; name: string; slug: string; created_at: string; updated_at: string }\n\n// ── Mappers ────────────────────────────────────────────────────────────────────\n\nfunction mapProfile(r: RowProfile): Profile {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n defaultLocale: r.default_locale, version: r.version,\n profileJson: JSON.parse(r.profile_json) as ProfileConfig,\n createdAt: r.created_at, updatedAt: r.updated_at,\n }\n}\n\nfunction mapConsent(r: RowConsent): ConsentDbRecord {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id,\n profileVersion: r.profile_version, locale: r.locale,\n consentJson: JSON.parse(r.consent_json) as ConsentValue,\n gpcDetected: r.gpc_detected, source: r.source as ConsentDbRecord['source'],\n createdAt: r.created_at, updatedAt: r.updated_at,\n ...(r.age_verified != null ? { ageVerified: r.age_verified } : {}),\n ...(r.parental_consent_token != null ? { parentalConsentToken: r.parental_consent_token } : {}),\n ...(r.tcf_string != null ? { tcfString: r.tcf_string } : {}),\n }\n}\n\nfunction mapHistory(r: RowConsentHistory): ConsentHistoryEntry {\n return {\n id: r.id, tenantId: r.tenant_id, consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'], createdAt: r.created_at,\n }\n}\n\nfunction mapVisitor(r: RowVisitor): Visitor {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n ...(r.country != null ? { country: r.country } : {}),\n ...(r.region != null ? { region: r.region } : {}),\n ...(r.city != null ? { city: r.city } : {}),\n ...(r.ip_hash != null ? { ipHash: r.ip_hash } : {}),\n ...(r.user_agent_hash != null ? { userAgentHash: r.user_agent_hash } : {}),\n firstSeen: r.first_seen, lastSeen: r.last_seen,\n }\n}\n\nfunction mapUser(r: RowUser): AdminUser {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name, email: r.email,\n passwordHash: r.password_hash, isActive: r.is_active,\n createdAt: r.created_at, updatedAt: r.updated_at,\n totpEnabled: r.totp_enabled ?? false,\n ...(r.totp_secret != null ? { totpSecret: r.totp_secret } : {}),\n }\n}\n\nfunction mapRole(r: RowRole): Role {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapPermission(r: RowPermission): Permission {\n return {\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapAuditLog(r: RowAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n createdAt: r.created_at,\n }\n}\n\n// ── Adapter ────────────────────────────────────────────────────────────────────\n\nexport class PostgreSQLAdapter implements StorageAdapter {\n private pool!: Pool\n\n constructor(private config: StorageConfig) {}\n\n private async q<T>(sql: string, params: (string | number | boolean | null)[] = []): Promise<T[]> {\n const result: QueryResult<T & object> = await this.pool.query<T & object>(sql, params as unknown[])\n return result.rows\n }\n\n async connect(): Promise<void> {\n type PgModule = { Pool: new (cfg: Record<string, unknown>) => Pool }\n const pg = (await import('pg')) as { default: PgModule }\n const PgPool = pg.default.Pool\n const cfg = this.config\n const poolCfg: Record<string, unknown> = cfg.uri\n ? { connectionString: cfg.uri }\n : {\n host: cfg.host ?? 'localhost',\n port: cfg.port ?? 5432,\n user: cfg.user ?? 'postgres',\n password: cfg.password ?? '',\n database: cfg.database ?? cfg.dbName ?? 'consenti',\n }\n this.pool = new PgPool(poolCfg)\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.pool.end()\n }\n\n async migrate(): Promise<void> {\n const client: PoolClient = await this.pool.connect()\n try {\n let version = 0\n try {\n const res = await client.query<{ version: number }>('SELECT version FROM schema_version ORDER BY version DESC LIMIT 1')\n version = res.rows[0]?.version ?? 0\n } catch {\n // schema_version doesn't exist — fresh install\n }\n\n if (version === 0) {\n await client.query('BEGIN')\n try {\n for (const stmt of SCHEMA_SQL_POSTGRES.split(';').map(s => s.trim()).filter(Boolean)) {\n await client.query(stmt)\n }\n await this.seedTx(client)\n await client.query('INSERT INTO schema_version (version) VALUES (2) ON CONFLICT DO NOTHING')\n await client.query('COMMIT')\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n }\n return\n }\n\n if (version < 2) {\n await client.query('BEGIN')\n try {\n const alters = [\n 'ALTER TABLE consent_records ADD COLUMN IF NOT EXISTS age_verified BOOLEAN NOT NULL DEFAULT FALSE',\n 'ALTER TABLE consent_records ADD COLUMN IF NOT EXISTS parental_consent_token TEXT',\n 'ALTER TABLE consent_records ADD COLUMN IF NOT EXISTS tcf_string TEXT',\n 'ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_secret TEXT',\n 'ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_enabled BOOLEAN NOT NULL DEFAULT FALSE',\n ]\n for (const stmt of alters) await client.query(stmt)\n await client.query('INSERT INTO schema_version (version) VALUES (2) ON CONFLICT DO NOTHING')\n await client.query('COMMIT')\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n }\n }\n\n if (version < 3) {\n await client.query('BEGIN')\n try {\n await client.query('ALTER TABLE user_roles ADD COLUMN IF NOT EXISTS tenant_id TEXT NOT NULL DEFAULT \\'default\\'')\n await client.query('INSERT INTO schema_version (version) VALUES (3) ON CONFLICT DO NOTHING')\n await client.query('COMMIT')\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n }\n }\n } finally {\n client.release()\n }\n }\n\n private async seedTx(client: PoolClient): Promise<void> {\n await client.query(\n 'INSERT INTO tenants (id, name, slug) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING',\n [SEED_TENANT.id, SEED_TENANT.name, SEED_TENANT.slug],\n )\n for (const p of SEED_PERMISSIONS) {\n await client.query(\n 'INSERT INTO permissions (id, name, description) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING',\n [p.id, p.name, p.description],\n )\n }\n for (const r of SEED_ROLES) {\n await client.query(\n 'INSERT INTO roles (id, tenant_id, name, description) VALUES ($1,$2,$3,$4) ON CONFLICT DO NOTHING',\n [r.id, r.tenantId, r.name, r.description],\n )\n }\n for (const { roleId, permissionId } of SEED_ROLE_PERMISSIONS) {\n await client.query(\n 'INSERT INTO role_permissions VALUES ($1,$2) ON CONFLICT DO NOTHING',\n [roleId, permissionId],\n )\n }\n }\n\n // ── Profiles ─────────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomUUID()\n const rows = await this.q<RowProfile>(\n `INSERT INTO profiles (id, tenant_id, name, default_locale, profile_json)\n VALUES ($1,$2,$3,$4,$5::jsonb) RETURNING *`,\n [id, data.tenantId, data.name, data.defaultLocale, JSON.stringify(data.profileJson)],\n )\n const row = rows[0]\n if (!row) throw new Error('Profile creation failed')\n return mapProfile(row)\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const sets: string[] = ['version = version + 1', 'updated_at = NOW()']\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.defaultLocale != null){ sets.push(`default_locale=$${idx++}`);vals.push(data.defaultLocale) }\n if (data.profileJson != null) { sets.push(`profile_json=$${idx++}::jsonb`);vals.push(JSON.stringify(data.profileJson)) }\n vals.push(id)\n const rows = await this.q<RowProfile>(\n `UPDATE profiles SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`Profile ${id} not found`)\n return mapProfile(row)\n }\n\n async deleteProfile(id: string): Promise<void> {\n await this.q('DELETE FROM profiles WHERE id=$1', [id])\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=$1', [id])\n return rows[0] ? mapProfile(rows[0]) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE tenant_id=$1 ORDER BY created_at DESC', [tenantId])\n return rows.map(mapProfile)\n }\n\n // ── Consents ─────────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomUUID()\n const rows = await this.q<RowConsent>(\n `INSERT INTO consent_records (id, tenant_id, visitor_id, profile_id, profile_version, locale, consent_json, gpc_detected, source)\n VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9) RETURNING *`,\n [id, data.tenantId, data.visitorId, data.profileId, data.profileVersion,\n data.locale, JSON.stringify(data.consentJson), data.gpcDetected, data.source],\n )\n const row = rows[0]\n if (!row) throw new Error('Consent creation failed')\n await this.writeHistory(id, data.tenantId, data.visitorId, null, data.consentJson, 'created')\n return mapConsent(row)\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const sets: string[] = ['consent_json=$1::jsonb', 'updated_at=NOW()']\n const vals: (string | number | boolean | null)[] = [JSON.stringify(data.consentJson)]\n let idx = 2\n if (data.locale != null) { sets.push(`locale=$${idx++}`); vals.push(data.locale) }\n if (data.gpcDetected != null) { sets.push(`gpc_detected=$${idx++}`); vals.push(data.gpcDetected) }\n vals.push(visitorId)\n const rows = await this.q<RowConsent>(\n `UPDATE consent_records SET ${sets.join(',')} WHERE visitor_id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`Consent for visitor ${visitorId} not found`)\n await this.writeHistory(row.id, row.tenant_id, visitorId, existing.consentJson, data.consentJson, 'updated')\n return mapConsent(row)\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n await this.q('DELETE FROM consent_history WHERE visitor_id=$1', [visitorId])\n await this.q('DELETE FROM consent_records WHERE visitor_id=$1', [visitorId])\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE visitor_id=$1', [visitorId])\n return rows[0] ? mapConsent(rows[0]) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n const { sql, params, nextIdx } = this.buildConsentQuery(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const rows = await this.q<RowConsent>(\n `${sql} ORDER BY created_at DESC LIMIT $${nextIdx} OFFSET $${nextIdx + 1}`,\n [...params, limit, (page - 1) * limit],\n )\n return rows.map(mapConsent)\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const { sql, params } = this.buildConsentQuery(filters)\n const rows = await this.q<RowConsent>(`${sql} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapConsent(r)\n }\n\n private buildConsentQuery(f: ConsentFilters): { sql: string; params: (string | number | boolean | null)[]; nextIdx: number } {\n let sql = 'SELECT * FROM consent_records WHERE tenant_id=$1'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n let idx = 2\n if (f.profileId != null) { sql += ` AND profile_id=$${idx++}`; params.push(f.profileId) }\n if (f.from != null) { sql += ` AND created_at>=$${idx++}`; params.push(f.from) }\n if (f.to != null) { sql += ` AND created_at<=$${idx++}`; params.push(f.to) }\n return { sql, params, nextIdx: idx }\n }\n\n private async writeHistory(\n consentRecordId: string, tenantId: string, visitorId: string,\n oldJson: ConsentValue | null, newJson: ConsentValue, action: string,\n ): Promise<void> {\n await this.q(\n `INSERT INTO consent_history (id, tenant_id, consent_record_id, visitor_id, old_json, new_json, action)\n VALUES ($1,$2,$3,$4,$5::jsonb,$6::jsonb,$7)`,\n [randomUUID(), tenantId, consentRecordId, visitorId,\n oldJson ? JSON.stringify(oldJson) : null, JSON.stringify(newJson), action],\n )\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = await this.q<RowConsentHistory>(\n 'SELECT * FROM consent_history WHERE visitor_id=$1 ORDER BY created_at ASC',\n [visitorId],\n )\n return rows.map(mapHistory)\n }\n\n // ── Visitors ─────────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomUUID()\n const rows = await this.q<RowVisitor>(\n `INSERT INTO visitors (id, tenant_id, visitor_id, country, region, city, ip_hash, user_agent_hash)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING *`,\n [id, data.tenantId, data.visitorId,\n data.country ?? null, data.region ?? null, data.city ?? null,\n data.ipHash ?? null, data.userAgentHash ?? null],\n )\n const row = rows[0]\n if (!row) throw new Error('Visitor creation failed')\n return mapVisitor(row)\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const sets: string[] = ['last_seen=NOW()']\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.country != null) { sets.push(`country=$${idx++}`); vals.push(data.country) }\n if (data.region != null) { sets.push(`region=$${idx++}`); vals.push(data.region) }\n if (data.city != null) { sets.push(`city=$${idx++}`); vals.push(data.city) }\n vals.push(visitorId)\n const rows = await this.q<RowVisitor>(\n `UPDATE visitors SET ${sets.join(',')} WHERE visitor_id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`Visitor ${visitorId} not found`)\n return mapVisitor(row)\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n await this.q('DELETE FROM visitors WHERE visitor_id=$1', [visitorId])\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE visitor_id=$1', [visitorId])\n return rows[0] ? mapVisitor(rows[0]) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n let sql = 'SELECT * FROM visitors WHERE tenant_id=$1'\n const params: (string | number | boolean | null)[] = [filters.tenantId]\n let idx = 2\n if (filters.from != null) { sql += ` AND first_seen>=$${idx++}`; params.push(filters.from) }\n if (filters.to != null) { sql += ` AND first_seen<=$${idx++}`; params.push(filters.to) }\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const rows = await this.q<RowVisitor>(\n `${sql} ORDER BY first_seen DESC LIMIT $${idx} OFFSET $${idx + 1}`,\n [...params, limit, (page - 1) * limit],\n )\n return rows.map(mapVisitor)\n }\n\n // ── Stats ─────────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const [[countRow], [gpcRow], [visitorRow]] = await Promise.all([\n this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1 AND gpc_detected=true', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*)::int AS total FROM visitors WHERE tenant_id=$1', [tenantId]),\n ])\n const total = Number(countRow?.total ?? 0)\n const gpcCount = Number(gpcRow?.total ?? 0)\n const visitors = Number(visitorRow?.total ?? 0)\n\n const statusRows = await this.q<RowStatus>(\n `SELECT val AS status, COUNT(*)::int AS count\n FROM consent_records cr,\n jsonb_each_text(cr.consent_json) AS kv(key, val)\n WHERE cr.tenant_id=$1\n GROUP BY val`,\n [tenantId],\n )\n let granted = 0, denied = 0, all = 0\n for (const r of statusRows) {\n all += Number(r.count)\n if (r.status === 'granted') granted = Number(r.count)\n if (r.status === 'denied') denied = Number(r.count)\n }\n return {\n totalConsents: total,\n acceptedPct: all > 0 ? Math.round((granted / all) * 100) : 0,\n rejectedPct: all > 0 ? Math.round((denied / all) * 100) : 0,\n totalVisitors: visitors,\n gpcDetectedCount: gpcCount,\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = await this.q<RowCookieStatus>(\n `SELECT kv.key AS cookie_id, kv.val AS status, COUNT(*)::int AS count\n FROM consent_records cr,\n jsonb_each_text(cr.consent_json) AS kv(key, val)\n WHERE cr.tenant_id=$1\n GROUP BY kv.key, kv.val`,\n [tenantId],\n )\n const result: CategoryStats = {}\n for (const r of rows) {\n if (!result[r.cookie_id]) result[r.cookie_id] = { granted: 0, denied: 0, objected: 0 }\n const entry = result[r.cookie_id]\n if (entry) {\n if (r.status === 'granted') entry.granted += Number(r.count)\n if (r.status === 'denied') entry.denied += Number(r.count)\n if (r.status === 'objected') entry.objected += Number(r.count)\n }\n }\n return result\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const from = new Date(Date.now() - days * 86400_000).toISOString()\n const rows = await this.q<RowDateCount>(\n `SELECT to_char(created_at, 'YYYY-MM-DD') AS date, COUNT(*)::int AS count\n FROM consent_records\n WHERE tenant_id=$1 AND created_at>=$2\n GROUP BY to_char(created_at, 'YYYY-MM-DD')\n ORDER BY date ASC`,\n [tenantId, from],\n )\n return rows.map(r => ({ date: r.date, count: Number(r.count) }))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const rows = await this.q<RowUser>(\n 'INSERT INTO users (id, tenant_id, name, email, password_hash) VALUES ($1,$2,$3,$4,$5) RETURNING *',\n [id, data.tenantId, data.name, data.email, data.passwordHash],\n )\n const row = rows[0]\n if (!row) throw new Error('User creation failed')\n return mapUser(row)\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const sets: string[] = ['updated_at=NOW()']\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.email != null) { sets.push(`email=$${idx++}`); vals.push(data.email) }\n if (data.passwordHash != null) { sets.push(`password_hash=$${idx++}`); vals.push(data.passwordHash) }\n if (data.isActive != null) { sets.push(`is_active=$${idx++}`); vals.push(data.isActive) }\n vals.push(id)\n const rows = await this.q<RowUser>(\n `UPDATE users SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`User ${id} not found`)\n return mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.q('DELETE FROM user_roles WHERE user_id=$1', [id])\n await this.q('DELETE FROM users WHERE id=$1', [id])\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=$1', [id])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE email=$1', [email])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE tenant_id=$1 ORDER BY created_at DESC', [tenantId])\n return rows.map(mapUser)\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const rows = tenantId\n ? await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM users WHERE tenant_id=$1', [tenantId])\n : await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM users')\n return Number(rows[0]?.total ?? 0)\n }\n\n // ── Roles & permissions ───────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE tenant_id=$1', [tenantId])\n return rows.map(mapRole)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n const rows = await this.q<RowRole>(\n 'INSERT INTO roles (id, tenant_id, name, description) VALUES ($1,$2,$3,$4) RETURNING *',\n [id, data.tenantId, data.name, data.description ?? null],\n )\n const row = rows[0]\n if (!row) throw new Error('Role creation failed')\n return mapRole(row)\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.description != null) { sets.push(`description=$${idx++}`); vals.push(data.description) }\n if (sets.length === 0) {\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE id=$1', [id])\n const row = rows[0]\n if (!row) throw new Error(`Role ${id} not found`)\n return mapRole(row)\n }\n vals.push(id)\n const rows = await this.q<RowRole>(`UPDATE roles SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`, vals)\n const row = rows[0]\n if (!row) throw new Error(`Role ${id} not found`)\n return mapRole(row)\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.q('DELETE FROM role_permissions WHERE role_id=$1', [id])\n await this.q('DELETE FROM user_roles WHERE role_id=$1', [id])\n await this.q('DELETE FROM roles WHERE id=$1', [id])\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = await this.q<RowPermission>('SELECT * FROM permissions ORDER BY name')\n return rows.map(mapPermission)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = await this.q<RowPermission>(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON p.id=rp.permission_id WHERE rp.role_id=$1',\n [roleId],\n )\n return rows.map(mapPermission)\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=$1 AND ur.tenant_id=$2',\n [userId, tenantId],\n )\n : await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=$1',\n [userId],\n )\n return rows.map(mapRole)\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n await this.q('INSERT INTO user_roles (user_id, role_id, tenant_id) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING', [userId, roleId, tenantId])\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n await this.q('DELETE FROM user_roles WHERE user_id=$1 AND role_id=$2 AND tenant_id=$3', [userId, roleId, tenantId])\n } else {\n await this.q('DELETE FROM user_roles WHERE user_id=$1 AND role_id=$2', [userId, roleId])\n }\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n await this.q('INSERT INTO role_permissions (role_id, permission_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [roleId, permissionId])\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n await this.q('DELETE FROM role_permissions WHERE role_id=$1 AND permission_id=$2', [roleId, permissionId])\n }\n\n // ── Audit logs ────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n await this.q(\n `INSERT INTO audit_logs (id, tenant_id, user_id, action, resource_type, resource_id, old_data, new_data)\n VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb)`,\n [randomUUID(), data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null],\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n const { sql, params, nextIdx } = this.buildAuditQuery(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const rows = await this.q<RowAuditLog>(\n `${sql} ORDER BY created_at DESC LIMIT $${nextIdx} OFFSET $${nextIdx + 1}`,\n [...params, limit, (page - 1) * limit],\n )\n return rows.map(mapAuditLog)\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const { sql, params } = this.buildAuditQuery(filters)\n const rows = await this.q<RowAuditLog>(`${sql} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapAuditLog(r)\n }\n\n private buildAuditQuery(f: AuditFilters): { sql: string; params: (string | number | boolean | null)[]; nextIdx: number } {\n let sql = 'SELECT * FROM audit_logs WHERE tenant_id=$1'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n let idx = 2\n if (f.action != null) { sql += ` AND action=$${idx++}`; params.push(f.action) }\n if (f.resourceType != null) { sql += ` AND resource_type=$${idx++}`; params.push(f.resourceType) }\n if (f.from != null) { sql += ` AND created_at>=$${idx++}`; params.push(f.from) }\n if (f.to != null) { sql += ` AND created_at<=$${idx++}`; params.push(f.to) }\n return { sql, params, nextIdx: idx }\n }\n\n // ── Stats extensions ───────────────────────────────────────────────────────────\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = await this.q<RowCountry>(\n `SELECT v.country, COUNT(*)::int AS count FROM consent_records cr\n JOIN visitors v ON v.visitor_id=cr.visitor_id\n WHERE cr.tenant_id=$1 AND v.country IS NOT NULL\n GROUP BY v.country ORDER BY count DESC LIMIT 50`,\n [tenantId],\n )\n return rows.map(r => ({ country: r.country, count: Number(r.count) }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const [t] = await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1', [tenantId])\n const [d] = await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1 AND gpc_detected=TRUE', [tenantId])\n const total = Number(t?.total ?? 0)\n const detected = Number(d?.total ?? 0)\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ───────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = await this.q<RowTenant>('SELECT * FROM tenants ORDER BY name ASC', [])\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n // ── API Keys ──────────────────────────────────────────────────────────────────\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const rows = await this.q<RowApiKey>(\n 'INSERT INTO api_keys (id, tenant_id, key_hash, name) VALUES ($1,$2,$3,$4) RETURNING *',\n [id, data.tenantId, data.keyHash, data.name],\n )\n const r = rows[0]\n if (!r) throw new Error('Failed to create API key')\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active, createdAt: r.created_at }\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE key_hash=$1 AND is_active=TRUE', [keyHash])\n const r = rows[0]\n if (!r) return null\n return { id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active, createdAt: r.created_at }\n }\n\n async revokeApiKey(id: string): Promise<void> {\n await this.q('UPDATE api_keys SET is_active=FALSE WHERE id=$1', [id])\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE tenant_id=$1 ORDER BY created_at DESC', [tenantId])\n return rows.map(r => ({ id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active, createdAt: r.created_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const rows = await this.q<RowTenant>(\n 'INSERT INTO tenants (id, name, slug, created_at, updated_at) VALUES ($1,$2,$3,$4,$5) RETURNING *',\n [id, data.name, data.slug, now, now],\n )\n const r = rows[0]\n if (!r) throw new Error('Failed to create tenant')\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const sets: string[] = ['updated_at=$1']\n const vals: (string | number)[] = [now]\n let idx = 2\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.slug != null) { sets.push(`slug=$${idx++}`); vals.push(data.slug) }\n vals.push(id)\n const rows = await this.q<RowTenant>(\n `UPDATE tenants SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`, vals,\n )\n const r = rows[0]\n if (!r) throw new Error(`Tenant ${id} not found`)\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n await this.q('DELETE FROM tenants WHERE id=$1', [id])\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const visitorRows = await this.q<{ visitor_id: string }>(\n 'SELECT visitor_id FROM consent_records WHERE created_at < $1', [cutoff]\n )\n if (visitorRows.length === 0) return 0\n const ids = visitorRows.map(r => r.visitor_id)\n const placeholders = ids.map((_: string, i: number) => `$${i + 1}`).join(',')\n await this.q(`DELETE FROM consent_history WHERE visitor_id IN (${placeholders})`, ids)\n await this.q(`DELETE FROM consent_records WHERE visitor_id IN (${placeholders})`, ids)\n return ids.length\n }\n\n // Template methods — not yet implemented for PostgreSQL adapter\n async createCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteCookieTemplate(): Promise<void> { throw new Error('Not implemented') }\n async getCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async getCookieTemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyCookieTemplate(): Promise<never> { throw new Error('Not implemented') }\n async createUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteUITemplate(): Promise<void> { throw new Error('Not implemented') }\n async getUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async getUITemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyUITemplate(): Promise<never> { throw new Error('Not implemented') }\n}\n","import { readFile, writeFile, rename, mkdir } from 'node:fs/promises'\nimport { randomUUID } from 'node:crypto'\nimport { dirname } from 'node:path'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n ProfileConfig,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n ServerCookieTemplate,\n ServerUITemplate,\n CreateCookieTemplateInput,\n UpdateCookieTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n} from '@consenti/types'\nimport {\n SEED_TENANT,\n SEED_PERMISSIONS,\n SEED_ROLES,\n SEED_ROLE_PERMISSIONS,\n} from '../seed-data'\n\nconst MAX_PAGE_SIZE = 500\n\ninterface UserRole { userId: string; roleId: string; tenantId: string }\ninterface RolePermission { roleId: string; permissionId: string }\n\ninterface JsonDb {\n _version: number\n tenants: Tenant[]\n profiles: Profile[]\n consents: ConsentDbRecord[]\n consent_history: ConsentHistoryEntry[]\n visitors: Visitor[]\n users: AdminUser[]\n roles: Role[]\n permissions: Permission[]\n user_roles: UserRole[]\n role_permissions: RolePermission[]\n audit_logs: AuditLog[]\n cookie_templates: ServerCookieTemplate[]\n ui_templates: ServerUITemplate[]\n api_keys: ApiKey[]\n}\n\nfunction emptyDb(): JsonDb {\n return {\n _version: 1,\n tenants: [],\n profiles: [],\n consents: [],\n consent_history: [],\n visitors: [],\n users: [],\n roles: [],\n permissions: [],\n user_roles: [],\n role_permissions: [],\n audit_logs: [],\n cookie_templates: [],\n ui_templates: [],\n api_keys: [],\n }\n}\n\nfunction randomProfileId(): string {\n return `prof_${randomUUID().replace(/-/g, '')}`\n}\n\nfunction page<T>(arr: T[], pageNum: number, limit: number): T[] {\n const l = Math.min(limit, MAX_PAGE_SIZE)\n const offset = (pageNum - 1) * l\n return arr.slice(offset, offset + l)\n}\n\nexport class JsonFileAdapter implements StorageAdapter {\n private db: JsonDb = emptyDb()\n private filePath: string\n // Steno-equivalent: serialised writes via promise chain.\n // Old settled promises are GC'd on reassignment — no memory leak.\n private writeQueue: Promise<void> = Promise.resolve()\n\n constructor(private config: StorageConfig) {\n this.filePath = config.path ?? './consenti-data.json'\n }\n\n private scheduleWrite(): void {\n this.writeQueue = this.writeQueue\n .then(async () => {\n await mkdir(dirname(this.filePath), { recursive: true })\n const tmp = `${this.filePath}.tmp`\n await writeFile(tmp, JSON.stringify(this.db), 'utf8')\n await rename(tmp, this.filePath)\n })\n .catch((err: unknown) => { console.warn('[Consenti] JSON store write failed:', err) })\n }\n\n async connect(): Promise<void> {\n try {\n const raw = await readFile(this.filePath, 'utf8')\n this.db = JSON.parse(raw) as JsonDb\n } catch {\n this.db = emptyDb()\n }\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.writeQueue\n }\n\n async migrate(): Promise<void> {\n this.seed()\n }\n\n private seed(): void {\n const now = new Date().toISOString()\n const { id, name, slug } = SEED_TENANT\n if (!this.db.tenants.find(t => t.id === id)) {\n this.db.tenants.push({ id, name, slug, createdAt: now, updatedAt: now })\n }\n for (const p of SEED_PERMISSIONS) {\n if (!this.db.permissions.find(x => x.id === p.id)) {\n this.db.permissions.push({ id: p.id, name: p.name, description: p.description })\n }\n }\n for (const r of SEED_ROLES) {\n if (!this.db.roles.find(x => x.id === r.id)) {\n this.db.roles.push({ id: r.id, tenantId: r.tenantId, name: r.name, description: r.description })\n }\n }\n for (const rp of SEED_ROLE_PERMISSIONS) {\n if (!this.db.role_permissions.find(x => x.roleId === rp.roleId && x.permissionId === rp.permissionId)) {\n this.db.role_permissions.push(rp)\n }\n }\n this.scheduleWrite()\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const now = new Date().toISOString()\n const profile: Profile = {\n id: randomProfileId(),\n tenantId: data.tenantId,\n name: data.name,\n defaultLocale: data.defaultLocale,\n version: 1,\n profileJson: data.profileJson as ProfileConfig,\n createdAt: now,\n updatedAt: now,\n }\n this.db.profiles.push(profile)\n this.scheduleWrite()\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const idx = this.db.profiles.findIndex(p => p.id === id)\n if (idx === -1) throw new Error(`Profile ${id} not found`)\n const existing = this.db.profiles[idx]!\n const updated: Profile = {\n ...existing,\n ...(data.name != null && { name: data.name }),\n ...(data.defaultLocale != null && { defaultLocale: data.defaultLocale }),\n ...(data.profileJson != null && { profileJson: data.profileJson as ProfileConfig }),\n version: existing.version + 1,\n updatedAt: new Date().toISOString(),\n }\n this.db.profiles[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.db.profiles = this.db.profiles.filter(p => p.id !== id)\n this.scheduleWrite()\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n return this.db.profiles.find(p => p.id === id) ?? null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n return this.db.profiles.filter(p => p.tenantId === tenantId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const now = new Date().toISOString()\n const id = randomUUID()\n const record: ConsentDbRecord = {\n id,\n tenantId: data.tenantId,\n visitorId: data.visitorId,\n profileId: data.profileId,\n profileVersion: data.profileVersion,\n locale: data.locale,\n consentJson: data.consentJson as ConsentValue,\n gpcDetected: data.gpcDetected ?? false,\n source: data.source as ConsentDbRecord['source'],\n createdAt: now,\n updatedAt: now,\n }\n this.db.consents.push(record)\n this.db.consent_history.push({\n id: randomUUID(),\n tenantId: data.tenantId,\n consentRecordId: id,\n visitorId: data.visitorId,\n oldJson: null,\n newJson: data.consentJson as ConsentValue,\n action: 'created',\n createdAt: now,\n })\n this.scheduleWrite()\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const idx = this.db.consents.findIndex(c => c.visitorId === visitorId)\n if (idx === -1) throw new Error(`Consent for visitor ${visitorId} not found`)\n const existing = this.db.consents[idx]!\n const now = new Date().toISOString()\n const updated: ConsentDbRecord = {\n ...existing,\n consentJson: data.consentJson as ConsentValue,\n ...(data.locale != null && { locale: data.locale }),\n ...(data.gpcDetected != null && { gpcDetected: data.gpcDetected }),\n updatedAt: now,\n }\n this.db.consents[idx] = updated\n this.db.consent_history.push({\n id: randomUUID(),\n tenantId: existing.tenantId,\n consentRecordId: existing.id,\n visitorId,\n oldJson: existing.consentJson,\n newJson: data.consentJson as ConsentValue,\n action: 'updated',\n createdAt: now,\n })\n this.scheduleWrite()\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.db.consent_history = this.db.consent_history.filter(h => h.visitorId !== visitorId)\n this.db.consents = this.db.consents.filter(c => c.visitorId !== visitorId)\n this.scheduleWrite()\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n return this.db.consents.find(c => c.visitorId === visitorId) ?? null\n }\n\n async getConsents(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n let rows = this.db.consents.filter(c => c.tenantId === filters.tenantId)\n if (filters.profileId) rows = rows.filter(c => c.profileId === filters.profileId)\n if (filters.from) rows = rows.filter(c => c.createdAt >= filters.from!)\n if (filters.to) rows = rows.filter(c => c.createdAt <= filters.to!)\n rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n return page(rows, filters.page ?? 1, filters.limit ?? 50)\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n for (const c of this.db.consents.filter(c => c.tenantId === filters.tenantId)) yield c\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n return this.db.consent_history\n .filter(h => h.visitorId === visitorId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const visitor: Visitor = {\n id: randomUUID(),\n tenantId: data.tenantId,\n visitorId: data.visitorId,\n firstSeen: now,\n lastSeen: now,\n ...(data.country != null && { country: data.country }),\n ...(data.region != null && { region: data.region }),\n ...(data.city != null && { city: data.city }),\n ...(data.ipHash != null && { ipHash: data.ipHash }),\n ...(data.userAgentHash != null && { userAgentHash: data.userAgentHash }),\n }\n this.db.visitors.push(visitor)\n this.scheduleWrite()\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const idx = this.db.visitors.findIndex(v => v.visitorId === visitorId)\n if (idx === -1) throw new Error(`Visitor ${visitorId} not found`)\n const existing = this.db.visitors[idx]!\n const updated: Visitor = {\n ...existing,\n lastSeen: data.lastSeen ?? new Date().toISOString(),\n ...(data.country != null && { country: data.country }),\n ...(data.region != null && { region: data.region }),\n ...(data.city != null && { city: data.city }),\n }\n this.db.visitors[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.db.visitors = this.db.visitors.filter(v => v.visitorId !== visitorId)\n this.scheduleWrite()\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n return this.db.visitors.find(v => v.visitorId === visitorId) ?? null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<Visitor[]> {\n let rows = this.db.visitors.filter(v => v.tenantId === filters.tenantId)\n if (filters.from) rows = rows.filter(v => v.firstSeen >= filters.from!)\n if (filters.to) rows = rows.filter(v => v.firstSeen <= filters.to!)\n rows.sort((a, b) => b.lastSeen.localeCompare(a.lastSeen))\n return page(rows, filters.page ?? 1, filters.limit ?? 50)\n }\n\n // ── Users ──────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const user: AdminUser = {\n id: randomUUID(),\n tenantId: data.tenantId,\n name: data.name,\n email: data.email,\n passwordHash: data.passwordHash,\n isActive: true,\n createdAt: now,\n updatedAt: now,\n }\n this.db.users.push(user)\n this.scheduleWrite()\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const idx = this.db.users.findIndex(u => u.id === id)\n if (idx === -1) throw new Error(`User ${id} not found`)\n const existing = this.db.users[idx]!\n const updated: AdminUser = {\n ...existing,\n ...(data.name != null && { name: data.name }),\n ...(data.email != null && { email: data.email }),\n ...(data.passwordHash != null && { passwordHash: data.passwordHash }),\n ...(data.isActive != null && { isActive: data.isActive }),\n ...('totpSecret' in data && data.totpSecret != null && { totpSecret: data.totpSecret }),\n ...(data.totpEnabled != null && { totpEnabled: data.totpEnabled }),\n updatedAt: new Date().toISOString(),\n }\n this.db.users[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteUser(id: string): Promise<void> {\n this.db.user_roles = this.db.user_roles.filter(ur => ur.userId !== id)\n this.db.users = this.db.users.filter(u => u.id !== id)\n this.scheduleWrite()\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n return this.db.users.find(u => u.id === id) ?? null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n return this.db.users.find(u => u.email === email) ?? null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n return this.db.users.filter(u => u.tenantId === tenantId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n return tenantId\n ? this.db.users.filter(u => u.tenantId === tenantId).length\n : this.db.users.length\n }\n\n // ── Roles & Permissions ────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n return this.db.roles.filter(r => r.tenantId === tenantId)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const role: Role = {\n id: randomUUID(),\n tenantId: data.tenantId,\n name: data.name,\n ...(data.description != null && { description: data.description }),\n }\n this.db.roles.push(role)\n this.scheduleWrite()\n return role\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const idx = this.db.roles.findIndex(r => r.id === id)\n if (idx === -1) throw new Error(`Role ${id} not found`)\n const updated: Role = {\n ...this.db.roles[idx]!,\n ...(data.name != null && { name: data.name }),\n ...(data.description != null && { description: data.description }),\n }\n this.db.roles[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteRole(id: string): Promise<void> {\n this.db.role_permissions = this.db.role_permissions.filter(rp => rp.roleId !== id)\n this.db.user_roles = this.db.user_roles.filter(ur => ur.roleId !== id)\n this.db.roles = this.db.roles.filter(r => r.id !== id)\n this.scheduleWrite()\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n return [...this.db.permissions].sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const permIds = new Set(\n this.db.role_permissions.filter(rp => rp.roleId === roleId).map(rp => rp.permissionId)\n )\n return this.db.permissions.filter(p => permIds.has(p.id))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n let urs = this.db.user_roles.filter(ur => ur.userId === userId)\n if (tenantId) urs = urs.filter(ur => ur.tenantId === tenantId)\n const roleIds = new Set(urs.map(ur => ur.roleId))\n return this.db.roles.filter(r => roleIds.has(r.id))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n if (!this.db.user_roles.find(ur => ur.userId === userId && ur.roleId === roleId && ur.tenantId === tenantId)) {\n this.db.user_roles.push({ userId, roleId, tenantId })\n this.scheduleWrite()\n }\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n this.db.user_roles = this.db.user_roles.filter(ur =>\n !(ur.userId === userId && ur.roleId === roleId && (tenantId == null || ur.tenantId === tenantId))\n )\n this.scheduleWrite()\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n if (!this.db.role_permissions.find(rp => rp.roleId === roleId && rp.permissionId === permissionId)) {\n this.db.role_permissions.push({ roleId, permissionId })\n this.scheduleWrite()\n }\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.db.role_permissions = this.db.role_permissions.filter(\n rp => !(rp.roleId === roleId && rp.permissionId === permissionId)\n )\n this.scheduleWrite()\n }\n\n // ── Audit ──────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const log: AuditLog = {\n id: randomUUID(),\n tenantId: data.tenantId,\n action: data.action,\n resourceType: data.resourceType,\n createdAt: new Date().toISOString(),\n ...(data.userId != null && { userId: data.userId }),\n ...(data.resourceId != null && { resourceId: data.resourceId }),\n ...(data.oldData != null && { oldData: data.oldData }),\n ...(data.newData != null && { newData: data.newData }),\n }\n this.db.audit_logs.push(log)\n this.scheduleWrite()\n }\n\n async getLogs(filters: AuditFilters): Promise<AuditLog[]> {\n let rows = this.db.audit_logs.filter(l => l.tenantId === filters.tenantId)\n if (filters.action) rows = rows.filter(l => l.action === filters.action)\n if (filters.resourceType) rows = rows.filter(l => l.resourceType === filters.resourceType)\n if (filters.from) rows = rows.filter(l => l.createdAt >= filters.from!)\n if (filters.to) rows = rows.filter(l => l.createdAt <= filters.to!)\n rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n return page(rows, filters.page ?? 1, filters.limit ?? 50)\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n let rows = this.db.audit_logs.filter(l => l.tenantId === filters.tenantId)\n if (filters.action) rows = rows.filter(l => l.action === filters.action)\n if (filters.from) rows = rows.filter(l => l.createdAt >= filters.from!)\n if (filters.to) rows = rows.filter(l => l.createdAt <= filters.to!)\n rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n for (const log of rows) yield log\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const consents = this.db.consents.filter(c => c.tenantId === tenantId)\n const recent = consents.slice(-500)\n let accepted = 0, rejected = 0\n for (const c of recent) {\n const vals = Object.values(c.consentJson)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n return {\n totalConsents: consents.length,\n gpcDetectedCount: consents.filter(c => c.gpcDetected).length,\n totalVisitors: this.db.visitors.filter(v => v.tenantId === tenantId).length,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const stats: CategoryStats = {}\n for (const c of this.db.consents.filter(c => c.tenantId === tenantId).slice(0, 5000)) {\n for (const [key, status] of Object.entries(c.consentJson)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const cutoff = new Date(Date.now() - days * 86_400_000).toISOString().slice(0, 10)\n const counts = new Map<string, number>()\n for (const c of this.db.consents.filter(c => c.tenantId === tenantId && c.createdAt.slice(0, 10) >= cutoff)) {\n const date = c.createdAt.slice(0, 10)\n counts.set(date, (counts.get(date) ?? 0) + 1)\n }\n return [...counts.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, count]) => ({ date, count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const consentVisitors = new Set(\n this.db.consents.filter(c => c.tenantId === tenantId).map(c => c.visitorId)\n )\n const counts = new Map<string, number>()\n for (const v of this.db.visitors.filter(v => consentVisitors.has(v.visitorId) && v.country)) {\n const c = v.country!\n counts.set(c, (counts.get(c) ?? 0) + 1)\n }\n return [...counts.entries()]\n .sort(([, a], [, b]) => b - a)\n .slice(0, 50)\n .map(([country, count]) => ({ country, count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const consents = this.db.consents.filter(c => c.tenantId === tenantId)\n const detected = consents.filter(c => c.gpcDetected).length\n const total = consents.length\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n return [...this.db.tenants].sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const tenant: Tenant = { id: randomUUID(), name: data.name, slug: data.slug, createdAt: now, updatedAt: now }\n this.db.tenants.push(tenant)\n this.scheduleWrite()\n return tenant\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const idx = this.db.tenants.findIndex(t => t.id === id)\n if (idx === -1) throw new Error(`Tenant ${id} not found`)\n const now = new Date().toISOString()\n const updated: Tenant = {\n ...this.db.tenants[idx]!,\n ...(data.name != null && { name: data.name }),\n ...(data.slug != null && { slug: data.slug }),\n updatedAt: now,\n }\n this.db.tenants[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.db.tenants = this.db.tenants.filter(t => t.id !== id)\n this.scheduleWrite()\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = new Set(\n this.db.consents.filter(c => c.createdAt < cutoff).map(c => c.visitorId)\n )\n if (toDelete.size === 0) return 0\n this.db.consent_history = this.db.consent_history.filter(h => !toDelete.has(h.visitorId))\n this.db.consents = this.db.consents.filter(c => !toDelete.has(c.visitorId))\n this.scheduleWrite()\n return toDelete.size\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const key: ApiKey = {\n id: randomUUID(),\n tenantId: data.tenantId,\n keyHash: data.keyHash,\n name: data.name,\n isActive: true,\n createdAt: new Date().toISOString(),\n }\n this.db.api_keys.push(key)\n this.scheduleWrite()\n return key\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n return this.db.api_keys.find(k => k.keyHash === keyHash && k.isActive) ?? null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n const key = this.db.api_keys.find(k => k.id === id)\n if (key) { key.isActive = false; this.scheduleWrite() }\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n return this.db.api_keys\n .filter(k => k.tenantId === tenantId)\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n }\n\n // ── Cookie Templates ───────────────────────────────────────────────────────\n\n async createCookieTemplate(data: CreateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const now = new Date().toISOString()\n const tpl: ServerCookieTemplate = {\n id: randomUUID(), tenantId: data.tenantId, name: data.name,\n cookies: data.cookies, createdAt: now, updatedAt: now,\n }\n this.db.cookie_templates.push(tpl)\n this.scheduleWrite()\n return tpl\n }\n\n async updateCookieTemplate(id: string, data: UpdateCookieTemplateInput): Promise<ServerCookieTemplate> {\n const idx = this.db.cookie_templates.findIndex(t => t.id === id)\n if (idx === -1) throw new Error('Cookie template not found')\n const now = new Date().toISOString()\n const updated: ServerCookieTemplate = {\n ...this.db.cookie_templates[idx]!,\n ...(data.name !== undefined && { name: data.name }),\n ...(data.cookies !== undefined && { cookies: data.cookies }),\n updatedAt: now,\n }\n this.db.cookie_templates[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteCookieTemplate(id: string): Promise<void> {\n this.db.cookie_templates = this.db.cookie_templates.filter(t => t.id !== id)\n this.scheduleWrite()\n }\n\n async getCookieTemplate(id: string): Promise<ServerCookieTemplate | null> {\n return this.db.cookie_templates.find(t => t.id === id) ?? null\n }\n\n async getCookieTemplates(tenantId: string): Promise<ServerCookieTemplate[]> {\n return this.db.cookie_templates\n .filter(t => t.tenantId === tenantId)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async copyCookieTemplate(id: string, newName: string): Promise<ServerCookieTemplate> {\n const src = await this.getCookieTemplate(id)\n if (!src) throw new Error('Cookie template not found')\n return this.createCookieTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n const tpl = { id: randomUUID(), tenantId, name, ...settings, createdAt: now, updatedAt: now } as ServerUITemplate\n this.db.ui_templates.push(tpl)\n this.scheduleWrite()\n return tpl\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const idx = this.db.ui_templates.findIndex(t => t.id === id)\n if (idx === -1) throw new Error('UI template not found')\n const { name: _name, ...settings } = data\n const now = new Date().toISOString()\n const updated = {\n ...this.db.ui_templates[idx]!,\n ...(data.name !== undefined && { name: data.name }),\n ...settings,\n updatedAt: now,\n } as ServerUITemplate\n this.db.ui_templates[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.db.ui_templates = this.db.ui_templates.filter(t => t.id !== id)\n this.scheduleWrite()\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n return this.db.ui_templates.find(t => t.id === id) ?? null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n return this.db.ui_templates\n .filter(t => t.tenantId === tenantId)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src as ServerUITemplate & {\n mainBanner?: unknown; gpcBanner?: unknown; preferenceModal?: unknown\n }\n return this.createUITemplate({\n tenantId, name: newName,\n ...(mainBanner !== undefined && { mainBanner }),\n ...(gpcBanner !== undefined && { gpcBanner }),\n ...(preferenceModal !== undefined && { preferenceModal }),\n } as CreateUITemplateInput)\n }\n}\n","import type {\n StorageAdapter, Profile, CreateProfileInput, UpdateProfileInput,\n} from '@consenti/types'\n\nexport class ProfileRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateProfileInput): Promise<Profile> {\n return this.adapter.createProfile(data)\n }\n\n update(id: string, data: UpdateProfileInput): Promise<Profile> {\n return this.adapter.updateProfile(id, data)\n }\n\n delete(id: string): Promise<void> {\n return this.adapter.deleteProfile(id)\n }\n\n get(id: string): Promise<Profile | null> {\n return this.adapter.getProfile(id)\n }\n\n list(tenantId: string): Promise<Profile[]> {\n return this.adapter.getProfiles(tenantId)\n }\n}\n","import type {\n StorageAdapter, ConsentDbRecord, CreateConsentInput, UpdateConsentInput, ConsentFilters,\n} from '@consenti/types'\n\nexport class ConsentRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateConsentInput): Promise<ConsentDbRecord> {\n return this.adapter.createConsent(data)\n }\n\n update(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n return this.adapter.updateConsent(visitorId, data)\n }\n\n delete(visitorId: string): Promise<void> {\n return this.adapter.deleteConsent(visitorId)\n }\n\n get(visitorId: string): Promise<ConsentDbRecord | null> {\n return this.adapter.getConsent(visitorId)\n }\n\n list(filters: ConsentFilters): Promise<ConsentDbRecord[]> {\n return this.adapter.getConsents(filters)\n }\n}\n","import type {\n StorageAdapter, Visitor, CreateVisitorInput, UpdateVisitorInput,\n} from '@consenti/types'\n\nexport class VisitorRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateVisitorInput): Promise<Visitor> {\n return this.adapter.createVisitor(data)\n }\n\n update(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n return this.adapter.updateVisitor(visitorId, data)\n }\n\n delete(visitorId: string): Promise<void> {\n return this.adapter.deleteVisitor(visitorId)\n }\n\n get(visitorId: string): Promise<Visitor | null> {\n return this.adapter.getVisitor(visitorId)\n }\n}\n","import type {\n StorageAdapter, AuditLog, AuditFilters, CreateAuditLogInput,\n} from '@consenti/types'\n\nexport class AuditRepo {\n constructor(private adapter: StorageAdapter) {}\n\n log(data: CreateAuditLogInput): Promise<void> {\n return this.adapter.createLog(data)\n }\n\n list(filters: AuditFilters): Promise<AuditLog[]> {\n return this.adapter.getLogs(filters)\n }\n}\n","import type {\n StorageAdapter,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n} from '@consenti/types'\n\nexport class UserRepo {\n constructor(private adapter: StorageAdapter) {}\n\n getById(id: string): Promise<AdminUser | null> { return this.adapter.getUserById(id) }\n getByEmail(email: string): Promise<AdminUser | null> { return this.adapter.getUserByEmail(email) }\n list(tenantId: string): Promise<AdminUser[]> { return this.adapter.getUsers(tenantId) }\n create(data: CreateUserInput): Promise<AdminUser> { return this.adapter.createUser(data) }\n update(id: string, data: UpdateUserInput): Promise<AdminUser> { return this.adapter.updateUser(id, data) }\n delete(id: string): Promise<void> { return this.adapter.deleteUser(id) }\n getRoles(userId: string, tenantId?: string): Promise<Role[]> { return this.adapter.getUserRoles(userId, tenantId) }\n assignRole(userId: string, roleId: string, tenantId?: string): Promise<void> { return this.adapter.assignRole(userId, roleId, tenantId) }\n revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> { return this.adapter.revokeRole(userId, roleId, tenantId) }\n}\n","import type { Profile, LocaleTranslations, ProfileTranslations } from '@consenti/types'\n\nfunction mergeTranslations(base: LocaleTranslations, override: Partial<LocaleTranslations>): LocaleTranslations {\n return {\n mainBanner: { ...base.mainBanner, ...override.mainBanner },\n preferenceModal: {\n ...base.preferenceModal,\n ...override.preferenceModal,\n buttons: override.preferenceModal?.buttons ?? base.preferenceModal.buttons,\n categories: override.preferenceModal?.categories ?? base.preferenceModal.categories,\n },\n ...(override.gpcBanner != null || base.gpcBanner != null\n ? { gpcBanner: { ...(base.gpcBanner ?? base.mainBanner), ...override.gpcBanner } }\n : {}),\n }\n}\n\nexport function resolveLocale(\n translations: ProfileTranslations,\n requested: string,\n defaultLocale: string,\n): LocaleTranslations {\n const base = translations[defaultLocale]\n if (!base) throw new Error(`Default locale '${defaultLocale}' not found in translations`)\n\n if (requested === defaultLocale) return base\n\n const exact = translations[requested]\n if (exact) return mergeTranslations(base, exact)\n\n const lang = requested.split('-')[0] ?? ''\n const langMatch = lang ? translations[lang] : undefined\n if (langMatch) return mergeTranslations(base, langMatch)\n\n return base\n}\n\nexport function resolveProfile(\n profile: Profile,\n locale: string,\n): { profile: Profile; resolved: LocaleTranslations } {\n const translations = profile.profileJson.translations ?? {}\n const resolved = resolveLocale(translations, locale, profile.defaultLocale)\n return { profile, resolved }\n}\n\nexport function bumpVersion(profile: Profile): number {\n return profile.version + 1\n}\n","import type { Profile, CreateProfileInput, UpdateProfileInput, PublicProfileResponse, StorageAdapter, LocaleTextContent, ServerUITemplate, LocaleTranslations, ButtonStyle, ButtonAction } from '@consenti/types'\nimport { resolveLocale } from '../core/profile-engine'\nimport type { ProfileRepo } from '../repositories/profile.repo'\nimport type { AuditRepo } from '../repositories/audit.repo'\n\nfunction buildTranslations(template: ServerUITemplate, localeContents: Record<string, LocaleTextContent>): Record<string, LocaleTranslations> {\n const result: Record<string, LocaleTranslations> = {}\n for (const [locale, content] of Object.entries(localeContents)) {\n result[locale] = {\n mainBanner: {\n position: template.mainBanner.position as LocaleTranslations['mainBanner']['position'],\n overlayOpacity: template.mainBanner.overlayOpacity,\n showClose: template.mainBanner.showClose,\n headingTag: template.mainBanner.headingTag,\n heading: content.mainBanner.heading,\n htmlText: content.mainBanner.htmlText,\n buttons: template.mainBanner.buttons.map((btn, i) => ({\n text: content.mainBanner.buttonLabels[i] ?? btn.text,\n style: btn.type as ButtonStyle,\n action: btn.action as ButtonAction,\n ...(btn.cookies != null ? { cookies: btn.cookies as '*' | '!' | string[] } : {}),\n })),\n },\n gpcBanner: {\n position: template.gpcBanner.position as LocaleTranslations['mainBanner']['position'],\n overlayOpacity: template.gpcBanner.overlayOpacity,\n showClose: template.gpcBanner.showClose,\n headingTag: template.gpcBanner.headingTag,\n heading: content.gpcBanner.heading,\n htmlText: content.gpcBanner.htmlText,\n buttons: template.gpcBanner.buttons.map((btn, i) => ({\n text: content.gpcBanner.buttonLabels[i] ?? btn.text,\n style: btn.type as ButtonStyle,\n action: btn.action as ButtonAction,\n ...(btn.cookies != null ? { cookies: btn.cookies as '*' | '!' | string[] } : {}),\n })),\n },\n preferenceModal: {\n position: template.preferenceModal.position as 'left' | 'right' | 'center',\n overlayOpacity: template.preferenceModal.overlayOpacity,\n showClose: template.preferenceModal.showClose,\n persistent: template.preferenceModal.persistent,\n headingTag: template.preferenceModal.headingTag,\n heading: content.preferenceModal.heading ?? '',\n ...(content.preferenceModal.subheading ? { subheading: content.preferenceModal.subheading } : {}),\n ...(content.preferenceModal.htmlText ? { htmlText: content.preferenceModal.htmlText } : {}),\n buttons: template.preferenceModal.buttons.map((btn, i) => ({\n text: content.preferenceModal.buttonLabels[i] ?? btn.text,\n style: btn.type as ButtonStyle,\n action: btn.action as ButtonAction,\n ...(btn.cookies != null ? { cookies: btn.cookies as '*' | '!' | string[] } : {}),\n })),\n categories: template.preferenceModal.categories.map(cat => {\n const c = content.preferenceModal.categories.find(c => c.id === cat.id)\n return {\n id: cat.id,\n heading: c?.heading ?? cat.id,\n headingTag: cat.headingTag,\n htmlText: c?.htmlText ?? '',\n mandatory: cat.mandatory,\n type: cat.type,\n ...(cat.liEnabled ? { legitimateInterest: { enabled: true } } : {}),\n cookies: cat.cookies,\n }\n }),\n },\n }\n }\n return result\n}\n\nexport class ProfileService {\n constructor(\n private profiles: ProfileRepo,\n private audit: AuditRepo,\n private tenantId: string = 'default',\n private storage?: StorageAdapter,\n ) {}\n\n async create(input: Omit<CreateProfileInput, 'tenantId'>): Promise<Profile> {\n const profile = await this.profiles.create({ ...input, tenantId: this.tenantId })\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'profile.created',\n resourceType: 'profile',\n resourceId: profile.id,\n newData: profile,\n })\n return profile\n }\n\n async update(id: string, input: UpdateProfileInput): Promise<Profile> {\n const old = await this.profiles.get(id)\n const profile = await this.profiles.update(id, input)\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'profile.updated',\n resourceType: 'profile',\n resourceId: id,\n ...(old != null ? { oldData: old } : {}),\n newData: profile,\n })\n return profile\n }\n\n async delete(id: string): Promise<void> {\n const old = await this.profiles.get(id)\n await this.profiles.delete(id)\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'profile.deleted',\n resourceType: 'profile',\n resourceId: id,\n ...(old != null ? { oldData: old } : {}),\n })\n }\n\n async get(id: string, locale?: string): Promise<Profile | null> {\n const profile = await this.profiles.get(id)\n if (!profile) return null\n if (!locale) return profile\n const resolved = resolveLocale(profile.profileJson.translations ?? {}, locale, profile.defaultLocale)\n return {\n ...profile,\n profileJson: {\n ...profile.profileJson,\n translations: { [locale]: resolved },\n },\n }\n }\n\n async getResolved(id: string, locale?: string): Promise<PublicProfileResponse | null> {\n const profile = await this.profiles.get(id)\n if (!profile) return null\n const pj = profile.profileJson\n\n let cookies = pj.cookies ?? []\n let translations = pj.translations ?? {}\n\n if (pj.cookieTemplateId && this.storage) {\n const ct = await this.storage.getCookieTemplate(pj.cookieTemplateId)\n if (ct) cookies = ct.cookies\n }\n\n if (pj.uiTemplateId && pj.localeContents && this.storage) {\n const ut = await this.storage.getUITemplate(pj.uiTemplateId)\n if (ut) translations = buildTranslations(ut, pj.localeContents)\n }\n\n const currentLocale = locale ?? profile.defaultLocale\n const localeKeys = pj.localeContents ? Object.keys(pj.localeContents) : Object.keys(translations)\n const resolved = resolveLocale(translations, currentLocale, profile.defaultLocale)\n return {\n id: profile.id,\n version: profile.version,\n defaultLocale: profile.defaultLocale,\n currentLocale,\n locales: localeKeys,\n cookies,\n mainBanner: resolved.mainBanner,\n preferenceModal: resolved.preferenceModal,\n ...(resolved.gpcBanner != null ? { gpcBanner: resolved.gpcBanner } : {}),\n }\n }\n\n list(): Promise<Profile[]> {\n return this.profiles.list(this.tenantId)\n }\n}\n","import type {\n ConsentDbRecord,\n ConsentValue,\n ConsentVerifyResult,\n CreateConsentInput,\n Profile,\n Cookie,\n} from '@consenti/types'\n\nexport interface ConsentValidationResult {\n valid: boolean\n errors: string[]\n}\n\nexport function validateConsentInput(\n data: Pick<CreateConsentInput, 'consentJson'>,\n profile: Profile,\n): ConsentValidationResult {\n const errors: string[] = []\n for (const [cookieId, status] of Object.entries(data.consentJson)) {\n const cookie = (profile.profileJson.cookies ?? []).find(c => c.id === cookieId)\n if (!cookie) continue\n if (cookie.mandatory && status !== 'granted') {\n errors.push(`Cookie \"${cookieId}\" is mandatory and cannot be \"${status}\"`)\n }\n if (status === 'objected' && cookie.type !== 'legitimate_interest') {\n errors.push(`Cookie \"${cookieId}\" is type \"${cookie.type ?? 'consent'}\" — 'objected' is only valid for 'legitimate_interest'`)\n }\n }\n return { valid: errors.length === 0, errors }\n}\n\nexport function buildConsentValue(\n cookies: Profile['profileJson']['cookies'],\n submitted: ConsentValue,\n gpcDetected: boolean,\n regulation?: string,\n): ConsentValue {\n const result: ConsentValue = {}\n for (const cookie of (cookies ?? [])) {\n const id = cookie.id\n if (cookie.mandatory) {\n result[id] = 'granted'\n continue\n }\n // CPRA: GPC triggers Do Not Sell + Do Not Share; sensitive data requires explicit opt-in\n if (regulation === 'cpra') {\n if (cookie.cpraCategory === 'sensitive') {\n // Sensitive data is opt-in even under CPRA — treat like GDPR (deny by default)\n const status = submitted[id]\n result[id] = (status === 'granted' || status === 'denied' || status === 'objected') ? status : 'denied'\n continue\n }\n if (gpcDetected && (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing')) {\n result[id] = 'denied'\n continue\n }\n }\n // GDPR/CCPA/DPDPA: honor listenGpc flag\n if (gpcDetected && cookie.listenGpc && regulation !== 'dpdpa') {\n result[id] = 'denied'\n continue\n }\n const status = submitted[id]\n if (status === 'granted' || status === 'denied' || status === 'objected') {\n result[id] = status\n } else {\n result[id] = 'denied'\n }\n }\n return result\n}\n\nexport function verifyConsent(\n record: ConsentDbRecord,\n profile: Profile,\n): ConsentVerifyResult {\n const reasons: ConsentVerifyResult['reasons'] = []\n\n if (record.profileVersion < profile.version) {\n reasons.push('profile_version_mismatch')\n }\n\n if (isExpired(record, profile)) {\n reasons.push('consent_expired')\n }\n\n return {\n valid: reasons.length === 0,\n reasons,\n currentProfileVersion: profile.version,\n consentProfileVersion: record.profileVersion,\n }\n}\n\nexport function buildTcfPayload(\n cookies: Cookie[],\n consentValue: ConsentValue,\n): { vendorConsents: number[]; purposeConsents: number[] } {\n const vendorSet = new Set<number>()\n const purposeSet = new Set<number>()\n for (const cookie of cookies) {\n if (consentValue[cookie.id] !== 'granted') continue\n if (cookie.tcfVendorId) vendorSet.add(cookie.tcfVendorId)\n for (const p of cookie.tcfPurposes ?? []) purposeSet.add(p)\n }\n return {\n vendorConsents: [...vendorSet].sort((a, b) => a - b),\n purposeConsents: [...purposeSet].sort((a, b) => a - b),\n }\n}\n\nfunction isExpired(record: ConsentDbRecord, profile: Profile): boolean {\n const cookies = profile.profileJson.cookies ?? []\n const createdMs = new Date(record.createdAt).getTime()\n const now = Date.now()\n\n for (const cookie of cookies) {\n if (!cookie.expiry) continue\n const expiryMs = createdMs + cookie.expiry * 24 * 60 * 60 * 1000\n if (now > expiryMs) return true\n }\n return false\n}\n","// Simplified TC string encoder/decoder.\n// This implementation stores a base64url-encoded JSON payload for basic compatibility.\n// For full IAB TCF v2.2 compliance (binary bitfield encoding), use the iabtcf-core npm package.\n\nexport interface TcStringInput {\n cmpId: number\n cmpVersion: number\n consentScreen: number\n consentLanguage: string\n vendorListVersion: number\n purposeConsents: number[]\n vendorConsents: number[]\n}\n\nexport function encodeTcString(input: TcStringInput): string {\n const payload = {\n v: 2,\n ts: Math.floor(Date.now() / 100),\n cmpId: input.cmpId,\n cmpV: input.cmpVersion,\n cs: input.consentScreen,\n lang: input.consentLanguage.toUpperCase().slice(0, 2),\n vlV: input.vendorListVersion,\n pc: input.purposeConsents,\n vc: input.vendorConsents,\n }\n return Buffer.from(JSON.stringify(payload)).toString('base64url')\n}\n\nexport interface DecodedTcString {\n version: number\n cmpId: number\n consentLanguage: string\n purposeConsents: number[]\n vendorConsents: number[]\n created: number\n}\n\nexport function decodeTcString(tcString: string): DecodedTcString | null {\n try {\n const raw = JSON.parse(Buffer.from(tcString, 'base64url').toString('utf8')) as {\n v: number; cmpId: number; lang: string; pc: number[]; vc: number[]; ts: number\n }\n return {\n version: raw.v,\n cmpId: raw.cmpId,\n consentLanguage: raw.lang,\n purposeConsents: Array.isArray(raw.pc) ? raw.pc : [],\n vendorConsents: Array.isArray(raw.vc) ? raw.vc : [],\n created: raw.ts * 100,\n }\n } catch {\n return null\n }\n}\n","import type {\n ConsentDbRecord, ConsentVerifyResult, CreateConsentInput, UpdateConsentInput,\n ComplianceConfig, TcfConfig,\n} from '@consenti/types'\nimport { buildConsentValue, verifyConsent, validateConsentInput, buildTcfPayload } from '../core/consent-engine'\nimport { encodeTcString } from '../tcf/tc-string'\nimport { getGvl } from '../tcf/gvl-cache'\nimport type { ConsentRepo } from '../repositories/consent.repo'\nimport type { VisitorRepo } from '../repositories/visitor.repo'\nimport type { ProfileRepo } from '../repositories/profile.repo'\nimport type { AuditRepo } from '../repositories/audit.repo'\nimport type { PluginEngine } from '../core/plugin-engine'\nimport type { EventEmitter } from 'node:events'\n\nexport class ConsentService {\n constructor(\n private consents: ConsentRepo,\n private visitors: VisitorRepo,\n private profiles: ProfileRepo,\n private audit: AuditRepo,\n private tenantId: string = 'default',\n private compliance?: ComplianceConfig,\n private pluginEngine?: PluginEngine,\n private eventBus?: EventEmitter,\n private tcfConfig?: TcfConfig,\n ) {}\n\n async create(input: Omit<CreateConsentInput, 'tenantId'>): Promise<ConsentDbRecord> {\n const profile = await this.profiles.get(input.profileId)\n if (!profile) throw new Error(`Profile ${input.profileId} not found`)\n\n const regulation = profile.profileJson.regulation\n const profileCookies = profile.profileJson.cookies ?? []\n // GPC server-side enforcement — skip for DPDPA (no GPC opt-out signal recognised)\n let effectiveConsent = input.consentJson\n if (input.gpcDetected && this.compliance?.gpc !== false && regulation !== 'dpdpa') {\n effectiveConsent = { ...effectiveConsent }\n for (const cookie of profileCookies) {\n if (regulation === 'cpra') {\n // CPRA: deny sale + sharing on GPC; sensitive stays as submitted\n if (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing') {\n effectiveConsent[cookie.id] = 'denied'\n }\n } else if (cookie.listenGpc) {\n effectiveConsent[cookie.id] = 'denied'\n }\n }\n }\n\n const validation = validateConsentInput({ ...input, consentJson: effectiveConsent }, profile)\n if (!validation.valid) throw new Error(validation.errors.join('; '))\n\n const builtConsent = buildConsentValue(\n profileCookies,\n effectiveConsent,\n input.gpcDetected,\n regulation,\n )\n\n let tcfString: string | undefined\n if (this.tcfConfig?.enabled && profileCookies.some(c => c.tcfVendorId)) {\n const gvl = await getGvl()\n const { vendorConsents, purposeConsents } = buildTcfPayload(profileCookies, builtConsent)\n tcfString = encodeTcString({\n cmpId: this.tcfConfig.cmpId,\n cmpVersion: this.tcfConfig.cmpVersion,\n consentScreen: 1,\n consentLanguage: input.locale?.slice(0, 2) ?? 'en',\n vendorListVersion: gvl?.vendorListVersion ?? 0,\n purposeConsents,\n vendorConsents,\n })\n }\n\n let baseInput: CreateConsentInput = {\n ...input,\n tenantId: this.tenantId,\n consentJson: builtConsent,\n profileVersion: profile.version,\n ...(tcfString ? { tcfString } : {}),\n }\n if (this.pluginEngine) baseInput = await this.pluginEngine.runBeforeConsentSave(baseInput)\n\n const existing = await this.consents.get(input.visitorId)\n let record: ConsentDbRecord\n\n if (existing) {\n const updateData = await (this.pluginEngine\n ? this.pluginEngine.runBeforeConsentUpdate({ consentJson: builtConsent, locale: input.locale, gpcDetected: input.gpcDetected })\n : Promise.resolve({ consentJson: builtConsent, locale: input.locale, gpcDetected: input.gpcDetected }))\n record = await this.consents.update(input.visitorId, updateData)\n } else {\n record = await this.consents.create(baseInput)\n }\n\n await this.audit.log({\n tenantId: this.tenantId,\n action: existing ? 'consent.updated' : 'consent.created',\n resourceType: 'consent',\n resourceId: record.id,\n ...(existing != null ? { oldData: existing } : {}),\n newData: record,\n })\n\n if (existing) {\n this.eventBus?.emit('consent.updated', { previous: existing, current: record })\n await this.pluginEngine?.runAfterConsentUpdate(record)\n } else {\n this.eventBus?.emit('consent.created', record)\n await this.pluginEngine?.runAfterConsentSave(record)\n }\n\n return record\n }\n\n async update(visitorId: string, input: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.consents.get(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n\n const profile = await this.profiles.get(existing.profileId)\n if (!profile) throw new Error(`Profile ${existing.profileId} not found`)\n\n const regulation = profile.profileJson.regulation\n const profileCookies2 = profile.profileJson.cookies ?? []\n const effectiveGpc = input.gpcDetected ?? existing.gpcDetected\n let effectiveConsent = input.consentJson\n if (effectiveGpc && this.compliance?.gpc !== false && regulation !== 'dpdpa') {\n effectiveConsent = { ...effectiveConsent }\n for (const cookie of profileCookies2) {\n if (regulation === 'cpra') {\n if (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing') {\n effectiveConsent[cookie.id] = 'denied'\n }\n } else if (cookie.listenGpc) {\n effectiveConsent[cookie.id] = 'denied'\n }\n }\n }\n\n const builtConsent = buildConsentValue(\n profileCookies2,\n effectiveConsent,\n effectiveGpc,\n regulation,\n )\n\n const record = await this.consents.update(visitorId, { ...input, consentJson: builtConsent })\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'consent.updated',\n resourceType: 'consent',\n resourceId: record.id,\n oldData: existing,\n newData: record,\n })\n return record\n }\n\n get(visitorId: string): Promise<ConsentDbRecord | null> {\n return this.consents.get(visitorId)\n }\n\n async verify(visitorId: string): Promise<ConsentVerifyResult> {\n const record = await this.consents.get(visitorId)\n if (!record) return { valid: false, reasons: ['profile_version_mismatch'] }\n\n const profile = await this.profiles.get(record.profileId)\n if (!profile) return { valid: false, reasons: ['profile_version_mismatch'] }\n\n return verifyConsent(record, profile)\n }\n\n async erase(visitorId: string): Promise<void> {\n const existing = await this.consents.get(visitorId)\n await this.consents.delete(visitorId)\n await this.visitors.delete(visitorId)\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'consent.erased',\n resourceType: 'consent',\n resourceId: visitorId,\n ...(existing != null ? { oldData: existing } : {}),\n })\n }\n}\n","import type { Visitor, CreateVisitorInput } from '@consenti/types'\nimport { hashIp, hashUserAgent } from '../utils/crypto'\nimport type { VisitorRepo } from '../repositories/visitor.repo'\n\nexport class VisitorService {\n constructor(\n private visitors: VisitorRepo,\n private tenantId: string = 'default',\n ) {}\n\n async upsert(data: {\n visitorId: string\n ip?: string\n userAgent?: string\n country?: string\n region?: string\n city?: string\n }): Promise<Visitor> {\n const existing = await this.visitors.get(data.visitorId)\n if (existing) {\n return this.visitors.update(data.visitorId, {\n ...(data.country != null ? { country: data.country } : {}),\n ...(data.region != null ? { region: data.region } : {}),\n ...(data.city != null ? { city: data.city } : {}),\n })\n }\n const input: CreateVisitorInput = {\n tenantId: this.tenantId,\n visitorId: data.visitorId,\n ...(data.country != null ? { country: data.country } : {}),\n ...(data.region != null ? { region: data.region } : {}),\n ...(data.city != null ? { city: data.city } : {}),\n ...(data.ip ? { ipHash: hashIp(data.ip) } : {}),\n ...(data.userAgent ? { userAgentHash: hashUserAgent(data.userAgent) } : {}),\n }\n return this.visitors.create(input)\n }\n}\n","import type { SafeAdminUser, AdminUser, UpdateUserInput } from '@consenti/types'\nimport { hashPassword } from '../utils/crypto'\nimport type { UserRepo } from '../repositories/user.repo'\nimport type { AuditRepo } from '../repositories/audit.repo'\n\nexport class UserService {\n constructor(\n private users: UserRepo,\n private audit: AuditRepo,\n private tenantId: string = 'default',\n ) {}\n\n private safe(user: AdminUser): SafeAdminUser {\n const { passwordHash: _p, ...rest } = user\n return rest\n }\n\n async create(\n input: { name: string; email: string; password: string; roleId?: string },\n actorId?: string,\n ): Promise<SafeAdminUser> {\n const user = await this.users.create({\n tenantId: this.tenantId,\n name: input.name,\n email: input.email,\n passwordHash: hashPassword(input.password),\n })\n if (input.roleId) await this.users.assignRole(user.id, input.roleId)\n await this.audit.log({\n tenantId: this.tenantId,\n ...(actorId != null ? { userId: actorId } : {}),\n action: 'user.created',\n resourceType: 'user',\n resourceId: user.id,\n newData: this.safe(user),\n })\n return this.safe(user)\n }\n\n async update(\n id: string,\n input: { name?: string; email?: string; isActive?: boolean; password?: string },\n actorId?: string,\n ): Promise<SafeAdminUser> {\n const existing = await this.users.getById(id)\n if (!existing) throw new Error(`User ${id} not found`)\n const data: UpdateUserInput = {}\n if (input.name != null) data.name = input.name\n if (input.email != null) data.email = input.email\n if (input.isActive != null) data.isActive = input.isActive\n if (input.password) data.passwordHash = hashPassword(input.password)\n const updated = await this.users.update(id, data)\n await this.audit.log({\n tenantId: this.tenantId,\n ...(actorId != null ? { userId: actorId } : {}),\n action: 'user.updated',\n resourceType: 'user',\n resourceId: id,\n oldData: this.safe(existing),\n newData: this.safe(updated),\n })\n return this.safe(updated)\n }\n\n async delete(id: string, actorId?: string): Promise<void> {\n const existing = await this.users.getById(id)\n await this.users.delete(id)\n await this.audit.log({\n tenantId: this.tenantId,\n ...(actorId != null ? { userId: actorId } : {}),\n action: 'user.deleted',\n resourceType: 'user',\n resourceId: id,\n ...(existing != null ? { oldData: this.safe(existing) } : {}),\n })\n }\n\n async get(id: string): Promise<SafeAdminUser | null> {\n const user = await this.users.getById(id)\n return user ? this.safe(user) : null\n }\n\n async list(): Promise<SafeAdminUser[]> {\n const users = await this.users.list(this.tenantId)\n return users.map(u => this.safe(u))\n }\n\n getRoles(userId: string, tenantId?: string) { return this.users.getRoles(userId, tenantId) }\n assignRole(userId: string, roleId: string, tenantId?: string) { return this.users.assignRole(userId, roleId, tenantId) }\n revokeRole(userId: string, roleId: string, tenantId?: string) { return this.users.revokeRole(userId, roleId, tenantId) }\n}\n","import type { StorageAdapter, AdminUser } from '@consenti/types'\nimport { hashPassword, verifyPassword, signJwt, verifyJwt } from '../utils/crypto'\n\nconst LOCKOUT_WINDOW_MS = 15 * 60_000\nconst MAX_FAILED_ATTEMPTS = 5\n\nexport class LocalAuth {\n private loginAttempts = new Map<string, { count: number; resetAt: number }>()\n\n constructor(\n private storage: StorageAdapter,\n private secret: string,\n ) {\n setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of this.loginAttempts) {\n if (now >= entry.resetAt) this.loginAttempts.delete(key)\n }\n }, LOCKOUT_WINDOW_MS).unref()\n }\n\n async bootstrap(email: string, password: string): Promise<AdminUser | null> {\n const count = await this.storage.countUsers('default')\n if (count > 0) return null\n if (password.length < 12) {\n throw new Error('[Consenti] bootstrap: admin password must be at least 12 characters')\n }\n if (password.length < 16) {\n console.warn('[Consenti] bootstrap: admin password is shorter than 16 characters — consider using a longer password')\n }\n const passwordHash = hashPassword(password)\n const user = await this.storage.createUser({\n tenantId: 'default',\n name: 'Admin',\n email,\n passwordHash,\n })\n await this.storage.assignRole(user.id, 'role_super_admin')\n return user\n }\n\n async login(email: string, password: string): Promise<string | null> {\n const now = Date.now()\n const attempts = this.loginAttempts.get(email)\n if (attempts && now < attempts.resetAt && attempts.count >= MAX_FAILED_ATTEMPTS) return null\n\n const user = await this.storage.getUserByEmail(email)\n if (!user || !user.isActive || !verifyPassword(password, user.passwordHash)) {\n const entry = this.loginAttempts.get(email)\n if (!entry || now >= entry.resetAt) {\n this.loginAttempts.set(email, { count: 1, resetAt: now + LOCKOUT_WINDOW_MS })\n } else {\n entry.count += 1\n }\n return null\n }\n\n this.loginAttempts.delete(email)\n const roles = await this.storage.getUserRoles(user.id)\n const permSets = await Promise.all(roles.map(r => this.storage.getPermissionsForRole(r.id)))\n const permissions = [...new Set(permSets.flat().map(p => p.name))]\n return signJwt(\n { sub: user.id, email: user.email, roles: roles.map(r => r.name), permissions },\n this.secret,\n )\n }\n\n signToken(payload: { sub: string; email: string; roles: string[]; permissions: string[] }): string {\n return signJwt(payload, this.secret)\n }\n\n verify(token: string): Record<string, unknown> | null {\n return verifyJwt(token, this.secret)\n }\n\n async getUser(token: string): Promise<AdminUser | null> {\n const payload = this.verify(token)\n if (!payload) return null\n const id = payload['sub']\n if (typeof id !== 'string') return null\n return this.storage.getUserById(id)\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport class PayloadTooLargeError extends Error {\n constructor() { super('Payload Too Large') }\n}\n\nexport function getIp(req: IncomingMessage, trustedProxies?: string[]): string {\n const remoteIp = req.socket?.remoteAddress ?? ''\n if (!trustedProxies?.length) return remoteIp\n if (!trustedProxies.includes(remoteIp)) return remoteIp\n const fwd = req.headers['x-forwarded-for']\n if (typeof fwd === 'string') return fwd.split(',')[0]?.trim() ?? remoteIp\n if (Array.isArray(fwd)) return fwd[0]?.trim() ?? remoteIp\n return remoteIp\n}\n\nexport function json(status: number, data: unknown): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\nexport async function parseJsonBody<T = unknown>(request: Request): Promise<T | null> {\n const ct = request.headers.get('content-type') ?? ''\n if (!ct.includes('application/json')) return null\n try {\n return await request.json() as T\n } catch {\n return null\n }\n}\n\nexport function getQueryParam(url: URL, key: string): string | undefined {\n const val = url.searchParams.get(key)\n return val ?? undefined\n}\n\nexport function getQueryInt(url: URL, key: string, fallback: number): number {\n const val = url.searchParams.get(key)\n if (!val) return fallback\n const n = parseInt(val, 10)\n return isNaN(n) ? fallback : n\n}\n\nexport async function nodeRequestToFetch(\n req: IncomingMessage,\n maxBodyBytes = 1_048_576,\n trustedProxies?: string[],\n): Promise<Request> {\n const rawProto = req.headers['x-forwarded-proto']\n const proto = rawProto === 'https' ? 'https' : 'http'\n const host = req.headers['host'] ?? 'localhost'\n const url = new URL(req.url ?? '/', `${proto}://${host}`)\n\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (!value) continue\n if (Array.isArray(value)) {\n for (const v of value) headers.append(key, v)\n } else {\n headers.set(key, value)\n }\n }\n headers.set('x-real-ip', getIp(req, trustedProxies))\n\n let body: BodyInit | null = null\n const method = (req.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const chunks: Buffer[] = []\n let totalBytes = 0\n for await (const chunk of req) {\n totalBytes += (chunk as Buffer).length\n if (totalBytes > maxBodyBytes) throw new PayloadTooLargeError()\n chunks.push(chunk as Buffer)\n }\n if (chunks.length > 0) body = Buffer.concat(chunks)\n }\n\n return new Request(url.toString(), { method, headers, body })\n}\n\nexport async function fetchResponseToNode(response: Response, res: ServerResponse): Promise<void> {\n res.statusCode = response.status\n const setCookies: string[] =\n typeof (response.headers as { getSetCookie?: () => string[] }).getSetCookie === 'function'\n ? (response.headers as unknown as { getSetCookie: () => string[] }).getSetCookie()\n : (response.headers.get('set-cookie') ? [response.headers.get('set-cookie')!] : [])\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'set-cookie') return\n res.setHeader(key, value)\n })\n for (const cookie of setCookies) {\n res.appendHeader('set-cookie', cookie)\n }\n if (response.body) {\n const reader = response.body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n res.write(Buffer.from(value))\n }\n } finally {\n reader.releaseLock()\n }\n res.end()\n } else {\n res.end()\n }\n}\n","export function errorResponse(status: number, message: string, details?: unknown): Response {\n const body: Record<string, unknown> = { error: message }\n if (details != null && process.env['NODE_ENV'] !== 'production') {\n body['details'] = details\n }\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\nexport async function withErrorHandler(fn: () => Promise<Response>): Promise<Response> {\n try {\n return await fn()\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Internal Server Error'\n return errorResponse(500, 'Internal Server Error',\n process.env['NODE_ENV'] !== 'production' ? message : undefined)\n }\n}\n","import type { ProfileService } from '../../services/profile.service'\nimport { json } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\n\nexport function buildProfileRoutes(profiles: ProfileService) {\n return {\n 'GET /profiles/:id': async (_request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const id = params['id']\n if (!id) return errorResponse(400, 'Missing profile id')\n const result = await profiles.getResolved(id)\n if (!result) return errorResponse(404, 'Profile not found')\n return json(200, result)\n }),\n\n 'GET /profiles/:id/:locale': async (_request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const id = params['id']\n const locale = params['locale']\n if (!id) return errorResponse(400, 'Missing profile id')\n if (!locale) return errorResponse(400, 'Missing locale')\n const result = await profiles.getResolved(id, locale)\n if (!result) return errorResponse(404, 'Profile not found')\n return json(200, result)\n }),\n }\n}\n","import type { ConsentDbRecord, ConsentValue } from '@consenti/types'\n\nfunction buildCookieValue(consentJson: ConsentValue, timestamp: string): string {\n const entries = Object.entries(consentJson)\n .map(([k, v]) => `${k}:${v}`)\n .join('|')\n return `ConsentTimestamp:${timestamp}::${entries}`\n}\n\nexport function buildConsentSetCookie(record: ConsentDbRecord): string {\n const name = `consenti_${record.visitorId}_${record.profileId}`\n const value = buildCookieValue(record.consentJson, record.createdAt)\n const secure = process.env['NODE_ENV'] === 'production' ? '; Secure' : ''\n return `${name}=${value}; SameSite=Lax${secure}; Path=/; Max-Age=31536000`\n}\n\nexport function buildExpireSetCookie(visitorId: string, profileId: string): string {\n const name = `consenti_${visitorId}_${profileId}`\n const secure = process.env['NODE_ENV'] === 'production' ? '; Secure' : ''\n return `${name}=; SameSite=Lax${secure}; Path=/; Max-Age=0`\n}\n","function matchesAllowedOrigin(origin: string, allowedOrigins: string[]): boolean {\n let hostname: string\n try {\n hostname = new URL(origin).hostname\n } catch {\n return false\n }\n return allowedOrigins.some(pattern => {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(1) // '.example.com'\n return hostname === pattern.slice(2) || hostname.endsWith(suffix)\n }\n return hostname === pattern\n })\n}\n\nexport function buildCorsHeaders(origin: string | null, allowedOrigins?: string[]): Record<string, string> {\n let allowOrigin = origin ?? '*'\n if (allowedOrigins && allowedOrigins.length > 0 && origin) {\n allowOrigin = matchesAllowedOrigin(origin, allowedOrigins) ? origin : 'null'\n }\n return {\n 'Access-Control-Allow-Origin': allowOrigin,\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n 'Access-Control-Max-Age': '86400',\n }\n}\n\nexport function handleCors(request: Request): Response | null {\n if (request.method === 'OPTIONS') {\n const origin = request.headers.get('origin')\n return new Response(null, {\n status: 204,\n headers: buildCorsHeaders(origin),\n })\n }\n return null\n}\n\nexport function withCors(response: Response, request: Request): Response {\n const origin = request.headers.get('origin')\n const cors = buildCorsHeaders(origin)\n const newHeaders = new Headers(response.headers)\n for (const [k, v] of Object.entries(cors)) newHeaders.set(k, v)\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n })\n}\n\nexport function checkOriginAllowed(request: Request, allowedOrigins: string[]): boolean {\n if (allowedOrigins.length === 0) return true\n const origin = request.headers.get('origin')\n if (!origin) return true // non-browser / server-side calls always allowed\n return matchesAllowedOrigin(origin, allowedOrigins)\n}\n\nexport function addSecurityHeaders(res: Response): Response {\n const h = new Headers(res.headers)\n h.set('X-Content-Type-Options', 'nosniff')\n h.set('X-Frame-Options', 'DENY')\n h.set('Referrer-Policy', 'no-referrer')\n h.set('Permissions-Policy', 'interest-cohort=()')\n return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h })\n}\n","import type { CreateConsentInput, UpdateConsentInput, ConsentValue } from '@consenti/types'\n\nexport interface ValidationResult {\n valid: boolean\n error?: string\n}\n\nconst VALID_STATUSES = new Set(['granted', 'denied', 'objected'])\n\nfunction validateConsentJsonValues(consentJson: unknown): ValidationResult {\n const entries = Object.entries(consentJson as Record<string, unknown>)\n for (const [key, val] of entries) {\n if (!key) return { valid: false, error: 'consentJson keys must be non-empty strings' }\n if (!VALID_STATUSES.has(val as string)) {\n return { valid: false, error: `consentJson value \"${String(val)}\" is invalid; must be granted, denied, or objected` }\n }\n }\n return { valid: true }\n}\n\nexport function validateCreateConsent(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n const b = body as Record<string, unknown>\n if (typeof b['profileId'] !== 'string' || !b['profileId']) return { valid: false, error: 'profileId is required' }\n if (!b['consentJson'] || typeof b['consentJson'] !== 'object' || Array.isArray(b['consentJson'])) {\n return { valid: false, error: 'consentJson must be an object' }\n }\n return validateConsentJsonValues(b['consentJson'])\n}\n\nexport function validateUpdateConsent(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n const b = body as Record<string, unknown>\n if (!b['consentJson'] || typeof b['consentJson'] !== 'object' || Array.isArray(b['consentJson'])) {\n return { valid: false, error: 'consentJson must be an object' }\n }\n return validateConsentJsonValues(b['consentJson'])\n}\n\nexport function castCreateConsent(body: Record<string, unknown>, visitorId: string, tenantId: string): CreateConsentInput {\n return {\n tenantId,\n visitorId,\n profileId: body['profileId'] as string,\n profileVersion: typeof body['profileVersion'] === 'number' ? body['profileVersion'] : 1,\n locale: typeof body['locale'] === 'string' ? body['locale'] : 'en',\n consentJson: body['consentJson'] as ConsentValue,\n gpcDetected: Boolean(body['gpcDetected']),\n source: (body['source'] as CreateConsentInput['source']) ?? 'banner',\n }\n}\n\nexport function castUpdateConsent(body: Record<string, unknown>): UpdateConsentInput {\n const result: UpdateConsentInput = {\n consentJson: body['consentJson'] as ConsentValue,\n }\n if (typeof body['locale'] === 'string') result.locale = body['locale']\n if (body['gpcDetected'] != null) result.gpcDetected = Boolean(body['gpcDetected'])\n return result\n}\n","import type { ConsentService } from '../../services/consent.service'\nimport type { VisitorService } from '../../services/visitor.service'\nimport type { ProfileService } from '../../services/profile.service'\nimport { parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { buildConsentSetCookie, buildExpireSetCookie } from '../../utils/cookie'\nimport { checkOriginAllowed } from '../../middleware/cors.middleware'\nimport {\n validateCreateConsent, validateUpdateConsent,\n castCreateConsent, castUpdateConsent,\n} from '../../validators/consent.validator'\nimport { randomUUID } from '../../utils/crypto'\n\n// Verify that the request's cookie contains a consenti_<visitorId>_* token,\n// proving the caller is the actual visitor who originally submitted consent.\nfunction assertVisitorOwnership(request: Request, visitorId: string): Response | null {\n const cookieHeader = request.headers.get('cookie') ?? ''\n const prefix = `consenti_${visitorId}_`\n const owned = cookieHeader.split(';').some(c => {\n const trimmed = c.trim()\n if (!trimmed.startsWith(prefix)) return false\n const val = trimmed.slice(trimmed.indexOf('=') + 1)\n return val.startsWith('ConsentTimestamp:')\n })\n if (!owned) return errorResponse(403, 'Forbidden')\n return null\n}\n\nexport function buildConsentRoutes(\n consents: ConsentService,\n visitors: VisitorService,\n profiles: ProfileService,\n resolveTenantId: (req: Request) => Promise<string> = async () => 'default',\n) {\n return {\n 'POST /consent': async (request: Request, _params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const body = await parseJsonBody(request)\n const validation = validateCreateConsent(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid request')\n\n const b = body as Record<string, unknown>\n const profileId = typeof b['profileId'] === 'string' ? b['profileId'] : null\n if (!profileId) return errorResponse(400, 'profileId is required')\n\n // Layer 1: verify the profileId is real — blocks all requests with unknown profiles\n const profile = await profiles.get(profileId)\n if (!profile) return errorResponse(404, 'Profile not found')\n\n // Layer 3: enforce the profile's allowed-domains list\n const pjConfig = profile.profileJson as { allowedOrigins?: string[] }\n const allowedOrigins = pjConfig.allowedOrigins ?? []\n if (!checkOriginAllowed(request, allowedOrigins)) {\n return errorResponse(403, 'Origin not allowed for this profile')\n }\n\n const visitorId = typeof b['visitorId'] === 'string' ? b['visitorId'] : randomUUID()\n const ip = request.headers.get('x-real-ip') ?? ''\n const ua = request.headers.get('user-agent') ?? ''\n const tenantId = await resolveTenantId(request)\n\n await visitors.upsert({ visitorId, ip, userAgent: ua })\n const input = castCreateConsent(b, visitorId, tenantId)\n const record = await consents.create(input)\n\n const setCookie = buildConsentSetCookie(record)\n return new Response(JSON.stringify(record), {\n status: 201,\n headers: {\n 'Content-Type': 'application/json',\n 'Set-Cookie': setCookie,\n },\n })\n }),\n\n 'PUT /consent/:visitorId': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: only the visitor's own browser may update their consent\n const ownershipError = assertVisitorOwnership(request, visitorId)\n if (ownershipError) return ownershipError\n\n const body = await parseJsonBody(request)\n const validation = validateUpdateConsent(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid request')\n\n const input = castUpdateConsent(body as Record<string, unknown>)\n const record = await consents.update(visitorId, input)\n\n const setCookie = buildConsentSetCookie(record)\n return new Response(JSON.stringify(record), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Set-Cookie': setCookie,\n },\n })\n }),\n\n 'GET /consent/:visitorId': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: only the visitor's own browser may read their consent\n const ownershipError = assertVisitorOwnership(request, visitorId)\n if (ownershipError) return ownershipError\n\n const record = await consents.get(visitorId)\n if (!record) return errorResponse(404, 'Consent not found')\n return new Response(JSON.stringify(record), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n }),\n\n 'GET /consent/:visitorId/verify': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: cookie ownership (browser calls); server-side callers use admin auth upstream\n const ownershipError = assertVisitorOwnership(request, visitorId)\n if (ownershipError) return ownershipError\n\n const result = await consents.verify(visitorId)\n return new Response(JSON.stringify(result), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n }),\n\n 'DELETE /consent/:visitorId': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: only the visitor's own browser may erase their consent\n const ownershipError = assertVisitorOwnership(request, visitorId)\n if (ownershipError) return ownershipError\n\n const existing = await consents.get(visitorId)\n await consents.erase(visitorId)\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (existing) {\n headers['Set-Cookie'] = buildExpireSetCookie(visitorId, existing.profileId)\n }\n return new Response(JSON.stringify({ success: true }), { status: 200, headers })\n }),\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate } from '../../middleware/auth.middleware'\nimport type { RateLimiter } from '../../middleware/rate-limit.middleware'\nimport type { LocalAuth } from '../../auth/local.auth'\nimport {\n generatePkce, discoverOidcEndpoints, buildAuthorizationUrl,\n exchangeOidcCode, verifyOidcIdToken, extractEmailFromClaims,\n extractRolesFromClaims, storePkceState, consumePkceState,\n} from '../../auth/oidc.auth'\nimport { buildSamlMetadataXml, validateSamlResponse } from '../../auth/saml.auth'\nimport { generateTotpSecret, verifyTotp, totpQrUrl } from '../../auth/totp'\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n}\n\nexport function buildAdminAuthRoutes(\n localAuth: LocalAuth,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n authLimiter?: RateLimiter | null,\n) {\n function checkAuthLimit(req: Request): Response | null {\n if (!authLimiter) return null\n const ip = req.headers.get('x-real-ip') ?? ''\n if (!authLimiter.checkRequest(ip)) return errorResponse(429, 'Too Many Requests')\n return null\n }\n\n return {\n 'POST /auth/login': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const limited = checkAuthLimit(req)\n if (limited) return limited\n if (authConfig.mode !== 'local') return errorResponse(400, 'Login not available in this auth mode')\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['email'] !== 'string' || typeof b['password'] !== 'string') {\n return errorResponse(400, 'email and password are required')\n }\n const token = await localAuth.login(b['email'], b['password'])\n if (!token) return errorResponse(401, 'Invalid credentials')\n return json(200, { token })\n }),\n\n 'GET /auth/me': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n const roles = await storage.getUserRoles(dbUser.id)\n return json(200, {\n id: dbUser.id,\n email: dbUser.email,\n name: dbUser.name,\n isActive: dbUser.isActive,\n totpEnabled: dbUser.totpEnabled ?? false,\n roles: roles.map(r => r.name),\n permissions: user.permissions,\n })\n }),\n\n 'POST /auth/logout': async (_req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => json(200, { success: true })),\n\n // ── OIDC ────────────────────────────────────────────────────────────────────\n\n 'GET /auth/oidc/authorize': async (_req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (authConfig.mode !== 'oidc' || !authConfig.oidc) return errorResponse(400, 'OIDC not configured')\n const endpoints = await discoverOidcEndpoints(authConfig.oidc.issuer)\n const state = base64url(randomBytes(16))\n const nonce = base64url(randomBytes(16))\n const { verifier, challenge } = generatePkce()\n storePkceState(state, verifier, nonce)\n const url = buildAuthorizationUrl(authConfig.oidc, endpoints.authorization_endpoint, state, nonce, challenge)\n return new Response(null, { status: 302, headers: { Location: url } })\n }),\n\n 'GET /auth/oidc/callback': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (authConfig.mode !== 'oidc' || !authConfig.oidc) return errorResponse(400, 'OIDC not configured')\n const url = new URL(req.url)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n if (!code || !state) return errorResponse(400, 'Missing code or state')\n const pkce = consumePkceState(state)\n if (!pkce) return errorResponse(400, 'Invalid or expired state')\n const endpoints = await discoverOidcEndpoints(authConfig.oidc.issuer)\n const tokens = await exchangeOidcCode(authConfig.oidc, endpoints.token_endpoint, code, pkce.verifier)\n const claims = await verifyOidcIdToken(tokens.id_token, endpoints.jwks_uri)\n const email = extractEmailFromClaims(claims, authConfig.oidc.claimsMapping)\n const roles = extractRolesFromClaims(claims, authConfig.oidc.claimsMapping)\n // Upsert admin user from OIDC claims\n let user = await storage.getUserByEmail(email)\n if (!user) {\n user = await storage.createUser({ tenantId: 'default', name: email, email, passwordHash: '' })\n }\n if (roles.length > 0) {\n const allRoles = await storage.getRoles('default')\n for (const roleName of roles) {\n const role = allRoles.find(r => r.name === roleName)\n if (role) await storage.assignRole(user.id, role.id).catch(() => {})\n }\n }\n const userRoles = await storage.getUserRoles(user.id)\n const permissions: string[] = []\n for (const role of userRoles) {\n const perms = await storage.getPermissionsForRole(role.id)\n permissions.push(...perms.map(p => p.name))\n }\n const token = localAuth.signToken({\n sub: user.id, email: user.email,\n roles: userRoles.map(r => r.name),\n permissions: [...new Set(permissions)],\n })\n return json(200, { token })\n }),\n\n // ── SAML ────────────────────────────────────────────────────────────────────\n\n 'GET /auth/saml/metadata': async (_req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (authConfig.mode !== 'saml' || !authConfig.saml) return errorResponse(400, 'SAML not configured')\n const xml = buildSamlMetadataXml(authConfig.saml)\n return new Response(xml, { status: 200, headers: { 'Content-Type': 'application/xml' } })\n }),\n\n 'POST /auth/saml/acs': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const limited = checkAuthLimit(req)\n if (limited) return limited\n if (authConfig.mode !== 'saml' || !authConfig.saml) return errorResponse(400, 'SAML not configured')\n const params = new URLSearchParams(await req.text())\n const samlResponse = params.get('SAMLResponse')\n if (typeof samlResponse !== 'string') return errorResponse(400, 'Missing SAMLResponse')\n const info = await validateSamlResponse(samlResponse, authConfig.saml)\n if (!info) return errorResponse(401, 'SAML validation failed')\n let user = await storage.getUserByEmail(info.email)\n if (!user) {\n user = await storage.createUser({ tenantId: 'default', name: info.email, email: info.email, passwordHash: '' })\n }\n const userRoles = await storage.getUserRoles(user.id)\n const permissions: string[] = []\n for (const role of userRoles) {\n const perms = await storage.getPermissionsForRole(role.id)\n permissions.push(...perms.map(p => p.name))\n }\n const token = localAuth.signToken({\n sub: user.id, email: user.email,\n roles: userRoles.map(r => r.name),\n permissions: [...new Set(permissions)],\n })\n return json(200, { token })\n }),\n\n // ── TOTP ────────────────────────────────────────────────────────────────────\n\n 'POST /auth/totp/setup': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n const totpSecret = generateTotpSecret()\n const qrUrl = totpQrUrl(totpSecret, dbUser.email)\n await storage.updateUser(dbUser.id, { totpSecret, totpEnabled: false })\n return json(200, { qrUrl, message: 'Scan QR code, then verify with POST /auth/totp/verify' })\n }),\n\n 'POST /auth/totp/verify': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const limited = checkAuthLimit(req)\n if (limited) return limited\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n if (!dbUser.totpSecret) return errorResponse(400, 'TOTP not set up — call POST /auth/totp/setup first')\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['token'] !== 'string') return errorResponse(400, 'token is required')\n if (!verifyTotp(dbUser.totpSecret, b['token'])) return errorResponse(400, 'Invalid TOTP token')\n await storage.updateUser(dbUser.id, { totpEnabled: true })\n return json(200, { success: true, message: 'TOTP enabled successfully' })\n }),\n\n 'POST /auth/totp/disable': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n await storage.updateUser(dbUser.id, { totpSecret: null, totpEnabled: false })\n return json(200, { success: true, message: 'TOTP disabled' })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { verifyJwt } from '../utils/crypto'\nimport { errorResponse } from './error.middleware'\n\nexport interface AuthenticatedUser {\n sub: string\n email: string\n roles: string[]\n permissions: string[]\n}\n\nexport async function authenticate(\n req: Request,\n storage: StorageAdapter,\n config: AuthConfig,\n secret: string,\n): Promise<AuthenticatedUser | null> {\n const auth = req.headers.get('authorization')\n const token = auth?.startsWith('Bearer ') ? auth.slice(7) : null\n if (!token) return null\n\n if (config.mode === 'local' || config.mode === 'jwt') {\n const payload = verifyJwt(token, secret)\n if (!payload) return null\n const sub = payload['sub']\n const email = payload['email']\n const roles = payload['roles']\n const permissions = payload['permissions']\n if (typeof sub !== 'string' || typeof email !== 'string') return null\n return {\n sub,\n email,\n roles: Array.isArray(roles) ? (roles as string[]) : [],\n permissions: Array.isArray(permissions) ? (permissions as string[]) : [],\n }\n }\n\n if (config.mode === 'custom' && config.validateUser) {\n const user = await config.validateUser(req)\n if (!user) return null\n const roles = await storage.getUserRoles(user.id)\n const permSets = await Promise.all(roles.map(r => storage.getPermissionsForRole(r.id)))\n const permissions = [...new Set(permSets.flat().map(p => p.name))]\n return {\n sub: user.id,\n email: user.email,\n roles: roles.map(r => r.name),\n permissions,\n }\n }\n\n return null\n}\n\nexport function authError(\n user: AuthenticatedUser | null,\n permission?: string,\n): Response | null {\n if (!user) return errorResponse(401, 'Unauthorized')\n if (permission && !user.permissions.includes(permission)) return errorResponse(403, 'Forbidden')\n return null\n}\n","import { randomBytes, createHash } from 'node:crypto'\nimport type { OidcConfig } from '@consenti/types'\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n}\n\nfunction base64urlDecode(s: string): string {\n return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8')\n}\n\nexport interface PkceChallenge {\n verifier: string\n challenge: string\n}\n\nexport function generatePkce(): PkceChallenge {\n const verifier = base64url(randomBytes(32))\n const challenge = base64url(createHash('sha256').update(verifier).digest())\n return { verifier, challenge }\n}\n\ninterface OidcEndpoints {\n authorization_endpoint: string\n token_endpoint: string\n userinfo_endpoint?: string\n jwks_uri: string\n}\n\nexport async function discoverOidcEndpoints(issuer: string): Promise<OidcEndpoints> {\n const res = await fetch(`${issuer}/.well-known/openid-configuration`, {\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) throw new Error(`OIDC discovery failed for ${issuer}: ${res.status}`)\n return res.json() as Promise<OidcEndpoints>\n}\n\nexport function buildAuthorizationUrl(\n config: OidcConfig,\n authorizationEndpoint: string,\n state: string,\n nonce: string,\n codeChallenge: string,\n): string {\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: config.clientId,\n redirect_uri: config.redirectUri,\n scope: 'openid email profile',\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n })\n return `${authorizationEndpoint}?${params.toString()}`\n}\n\ninterface TokenResponse {\n id_token: string\n access_token: string\n token_type: string\n expires_in?: number\n}\n\nexport async function exchangeOidcCode(\n config: OidcConfig,\n tokenEndpoint: string,\n code: string,\n codeVerifier: string,\n): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: config.clientId,\n client_secret: config.clientSecret,\n redirect_uri: config.redirectUri,\n code,\n code_verifier: codeVerifier,\n })\n const res = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) {\n const err = await res.text()\n throw new Error(`OIDC token exchange failed: ${err}`)\n }\n return res.json() as Promise<TokenResponse>\n}\n\nexport interface OidcClaims {\n sub: string\n email?: string\n name?: string\n [key: string]: unknown\n}\n\nexport async function verifyOidcIdToken(idToken: string, jwksUri: string): Promise<OidcClaims> {\n const parts = idToken.split('.')\n if (parts.length !== 3) throw new Error('Invalid ID token format')\n\n const header = JSON.parse(base64urlDecode(parts[0]!)) as { kid?: string; alg?: string }\n const alg = header.alg ?? 'RS256'\n\n const res = await fetch(jwksUri, { signal: AbortSignal.timeout(10_000) })\n if (!res.ok) throw new Error(`Failed to fetch JWKS: ${res.status}`)\n type JwkEntry = JsonWebKey & { kid?: string; use?: string }\n const jwks = await res.json() as { keys: JwkEntry[] }\n\n const jwk: JwkEntry | undefined = header.kid\n ? jwks.keys.find(k => k.kid === header.kid)\n : (jwks.keys.find(k => k.use === 'sig') ?? jwks.keys[0])\n if (!jwk) throw new Error('No matching key in JWKS')\n\n let cryptoKey: CryptoKey\n if (alg === 'RS256' || alg === 'RS384' || alg === 'RS512') {\n const hashName = alg === 'RS256' ? 'SHA-256' : alg === 'RS384' ? 'SHA-384' : 'SHA-512'\n cryptoKey = await globalThis.crypto.subtle.importKey(\n 'jwk', jwk, { name: 'RSASSA-PKCS1-v1_5', hash: { name: hashName } }, false, ['verify'],\n )\n } else if (alg === 'ES256' || alg === 'ES384' || alg === 'ES512') {\n const namedCurve = alg === 'ES256' ? 'P-256' : alg === 'ES384' ? 'P-384' : 'P-521'\n cryptoKey = await globalThis.crypto.subtle.importKey(\n 'jwk', jwk, { name: 'ECDSA', namedCurve }, false, ['verify'],\n )\n } else {\n throw new Error(`Unsupported OIDC token algorithm: ${alg}`)\n }\n\n const sigInput = new TextEncoder().encode(`${parts[0]}.${parts[1]}`)\n const padding = (4 - (parts[2]!.length % 4)) % 4\n const sigB64 = parts[2]!.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat(padding)\n const sigBytes = Uint8Array.from(atob(sigB64), c => c.charCodeAt(0))\n\n const verifyAlg = alg.startsWith('ES')\n ? { name: 'ECDSA', hash: { name: `SHA-${alg.slice(2)}` } }\n : { name: 'RSASSA-PKCS1-v1_5' }\n\n const valid = await globalThis.crypto.subtle.verify(verifyAlg, cryptoKey, sigBytes, sigInput)\n if (!valid) throw new Error('ID token signature verification failed')\n\n const payload = JSON.parse(base64urlDecode(parts[1]!)) as OidcClaims\n const now = Math.floor(Date.now() / 1000)\n if (typeof payload['exp'] === 'number' && payload['exp'] < now) {\n throw new Error('ID token has expired')\n }\n\n return payload\n}\n\nexport function extractEmailFromClaims(claims: OidcClaims, mapping?: { email?: string }): string {\n const key = mapping?.email ?? 'email'\n const value = claims[key]\n if (typeof value !== 'string') throw new Error('OIDC token missing email claim')\n return value\n}\n\nexport function extractRolesFromClaims(\n claims: OidcClaims,\n mapping?: { roles?: string },\n): string[] {\n const key = mapping?.roles ?? 'consenti_roles'\n const value = claims[key]\n if (!Array.isArray(value)) return []\n return value.filter((v): v is string => typeof v === 'string')\n}\n\n// In-memory PKCE/state store. Replace with Redis or DB for multi-instance deployments.\nconst pendingStates = new Map<string, { verifier: string; nonce: string; expiresAt: number }>()\n\nsetInterval(() => {\n const now = Date.now()\n for (const [key, val] of pendingStates) {\n if (val.expiresAt < now) pendingStates.delete(key)\n }\n}, 5 * 60_000).unref()\n\nexport function storePkceState(state: string, verifier: string, nonce: string): void {\n pendingStates.set(state, { verifier, nonce, expiresAt: Date.now() + 10 * 60_000 })\n}\n\nexport function consumePkceState(\n state: string,\n): { verifier: string; nonce: string } | null {\n const entry = pendingStates.get(state)\n pendingStates.delete(state)\n if (!entry || entry.expiresAt < Date.now()) return null\n return { verifier: entry.verifier, nonce: entry.nonce }\n}\n","import type { SamlConfig } from '@consenti/types'\n\nfunction escXml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n}\n\nexport function buildSamlMetadataXml(config: SamlConfig): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n entityID=\"saml:sp:consenti\">\n <SPSSODescriptor\n protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"\n AuthnRequestsSigned=\"false\"\n WantAssertionsSigned=\"true\">\n <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>\n <AssertionConsumerService\n Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n Location=\"${escXml(config.callbackUrl)}\"\n index=\"0\"\n isDefault=\"true\"/>\n </SPSSODescriptor>\n</EntityDescriptor>`\n}\n\nexport interface SamlUserInfo {\n email: string\n roles: string[]\n}\n\n// Validates a SAML response using the `samlify` peer dependency.\n// Install: npm install samlify\nexport async function validateSamlResponse(\n samlResponse: string,\n config: SamlConfig,\n): Promise<SamlUserInfo | null> {\n let samlify: {\n ServiceProvider: (cfg: Record<string, unknown>) => {\n parseLoginResponse: (\n idp: unknown,\n type: string,\n req: { body: Record<string, string> },\n ) => Promise<{ extract: { nameID: string; attributes: Record<string, unknown> } }>\n }\n IdentityProvider: (cfg: Record<string, unknown>) => unknown\n }\n\n try {\n // @ts-expect-error -- samlify is an optional peer dependency; callers must install it\n samlify = (await import('samlify')) as unknown as typeof samlify\n } catch {\n console.warn('[Consenti] samlify is not installed. Install it to use SAML auth mode.')\n return null\n }\n\n try {\n const sp = samlify.ServiceProvider({\n entityID: 'saml:sp:consenti',\n assertionConsumerService: [\n {\n Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',\n Location: config.callbackUrl,\n },\n ],\n })\n const idp = samlify.IdentityProvider({\n entityID: config.issuer,\n singleSignOnService: [\n {\n Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',\n Location: config.entryPoint,\n },\n ],\n signingCert: config.cert,\n })\n const result = await sp.parseLoginResponse(idp, 'post', {\n body: { SAMLResponse: samlResponse },\n })\n const email = result.extract.nameID\n const rolesAttr = result.extract.attributes['roles']\n const roles = Array.isArray(rolesAttr)\n ? rolesAttr.filter((r): r is string => typeof r === 'string')\n : []\n return { email, roles }\n } catch (err) {\n console.warn('[Consenti] SAML response validation failed:', err)\n return null\n }\n}\n","import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto'\n\nconst BASE32_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'\n\nfunction base32Encode(buf: Buffer): string {\n let result = ''\n let bits = 0\n let value = 0\n for (const byte of buf) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n result += BASE32_CHARS[(value >>> (bits - 5)) & 0x1f]\n bits -= 5\n }\n }\n if (bits > 0) result += BASE32_CHARS[(value << (5 - bits)) & 0x1f]\n return result\n}\n\nfunction base32Decode(str: string): Buffer {\n const clean = str.toUpperCase().replace(/[^A-Z2-7]/g, '')\n const bytes: number[] = []\n let bits = 0\n let value = 0\n for (const char of clean) {\n const idx = BASE32_CHARS.indexOf(char)\n if (idx < 0) continue\n value = (value << 5) | idx\n bits += 5\n if (bits >= 8) {\n bytes.push((value >>> (bits - 8)) & 0xff)\n bits -= 8\n }\n }\n return Buffer.from(bytes)\n}\n\nfunction hotp(secret: Buffer, counter: number): string {\n const buf = Buffer.alloc(8)\n let c = counter\n for (let i = 7; i >= 0; i--) {\n buf[i] = c & 0xff\n c = Math.floor(c / 256)\n }\n const hmac = createHmac('sha1', secret).update(buf).digest()\n const offset = hmac[hmac.length - 1]! & 0x0f\n const code =\n ((hmac[offset]! & 0x7f) << 24) |\n ((hmac[offset + 1]! & 0xff) << 16) |\n ((hmac[offset + 2]! & 0xff) << 8) |\n (hmac[offset + 3]! & 0xff)\n return String(code % 1_000_000).padStart(6, '0')\n}\n\nexport function generateTotpSecret(): string {\n return base32Encode(randomBytes(20))\n}\n\nexport function generateTotp(secret: string, time = Date.now()): string {\n return hotp(base32Decode(secret), Math.floor(time / 30_000))\n}\n\nconst usedTokens = new Map<string, number>() // token → expiresAt\n\nsetInterval(() => {\n const now = Date.now()\n for (const [token, expiresAt] of usedTokens) {\n if (now >= expiresAt) usedTokens.delete(token)\n }\n}, 90_000).unref()\n\nexport function verifyTotp(secret: string, token: string, window = 1): boolean {\n if (usedTokens.has(token)) return false\n const counter = Math.floor(Date.now() / 30_000)\n const secretBuf = base32Decode(secret)\n const tokenBuf = Buffer.from(token)\n for (let i = -window; i <= window; i++) {\n const expected = Buffer.from(hotp(secretBuf, counter + i))\n if (expected.length === tokenBuf.length && timingSafeEqual(expected, tokenBuf)) {\n usedTokens.set(token, Date.now() + 90_000)\n return true\n }\n }\n return false\n}\n\nexport function totpQrUrl(secret: string, email: string, issuer = 'Consenti'): string {\n const label = encodeURIComponent(`${issuer}:${email}`)\n const params = new URLSearchParams({\n secret,\n issuer,\n algorithm: 'SHA1',\n digits: '6',\n period: '30',\n })\n return `otpauth://totp/${label}?${params.toString()}`\n}\n","import type { CreateProfileInput, UpdateProfileInput, ProfileConfig } from '@consenti/types'\n\nexport interface ValidationResult {\n valid: boolean\n error?: string\n}\n\nexport function validateCreateProfile(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || !b['name']) return { valid: false, error: 'name is required' }\n if (typeof b['defaultLocale'] !== 'string' || !b['defaultLocale']) return { valid: false, error: 'defaultLocale is required' }\n if (!b['profileJson'] || typeof b['profileJson'] !== 'object') return { valid: false, error: 'profileJson is required' }\n const pj = b['profileJson'] as Record<string, unknown>\n if (typeof pj['cookieTemplateId'] !== 'string' || !pj['cookieTemplateId']) {\n return { valid: false, error: 'profileJson.cookieTemplateId is required' }\n }\n if (typeof pj['uiTemplateId'] !== 'string' || !pj['uiTemplateId']) {\n return { valid: false, error: 'profileJson.uiTemplateId is required' }\n }\n return { valid: true }\n}\n\nexport function validateUpdateProfile(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n return { valid: true }\n}\n\nexport function castCreateProfile(body: Record<string, unknown>, tenantId: string): CreateProfileInput {\n return {\n tenantId,\n name: body['name'] as string,\n defaultLocale: body['defaultLocale'] as string,\n profileJson: body['profileJson'] as CreateProfileInput['profileJson'],\n }\n}\n\nexport function castUpdateProfile(body: Record<string, unknown>): UpdateProfileInput {\n const result: UpdateProfileInput = {}\n if (typeof body['name'] === 'string') result.name = body['name']\n if (typeof body['defaultLocale'] === 'string') result.defaultLocale = body['defaultLocale']\n if (body['profileJson'] != null) result.profileJson = body['profileJson'] as ProfileConfig\n return result\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport { validateCreateProfile, validateUpdateProfile, castCreateProfile, castUpdateProfile } from '../../validators/profile.validator'\nimport type { ProfileService } from '../../services/profile.service'\n\nexport function buildAdminProfileRoutes(\n service: ProfileService,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /profiles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await service.list())\n }),\n\n 'GET /profiles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const profile = await service.get(p['id'] ?? '')\n if (!profile) return errorResponse(404, 'Profile not found')\n return json(200, profile)\n }),\n\n 'POST /profiles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody(req)\n const validation = validateCreateProfile(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid body')\n const profile = await service.create(castCreateProfile(body as Record<string, unknown>, 'default'))\n return json(201, profile)\n }),\n\n 'PUT /profiles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n const body = await parseJsonBody(req)\n const validation = validateUpdateProfile(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid body')\n const profile = await service.update(id, castUpdateProfile(body as Record<string, unknown>))\n return json(200, profile)\n }),\n\n 'DELETE /profiles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:delete')\n if (denied) return denied\n await service.delete(p['id'] ?? '')\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, ConsentFilters } from '@consenti/types'\nimport { json, getQueryParam, getQueryInt } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminConsentRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, perm) }\n }\n\n return {\n 'GET /consents': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'consent:view')\n if (denied) return denied\n const url = new URL(req.url)\n const profileId = getQueryParam(url, 'profileId')\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const limit = getQueryInt(url, 'limit', 50)\n if (limit > 500) return errorResponse(400, 'limit must not exceed 500')\n const filters: ConsentFilters = {\n tenantId: 'default',\n page: getQueryInt(url, 'page', 1),\n limit,\n ...(profileId !== undefined ? { profileId } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n const records = await storage.getConsents(filters)\n return json(200, records)\n }),\n\n 'GET /consents/:visitorId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'consent:view')\n if (denied) return denied\n const record = await storage.getConsent(p['visitorId'] ?? '')\n if (!record) return errorResponse(404, 'Consent not found')\n return json(200, record)\n }),\n\n 'GET /consents/:visitorId/history': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'consent:view')\n if (denied) return denied\n const history = await storage.getConsentHistory(p['visitorId'] ?? '')\n return json(200, history)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, VisitorFilters } from '@consenti/types'\nimport { json, getQueryInt, getQueryParam } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminVisitorRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, perm) }\n }\n\n return {\n 'GET /visitors': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'visitor:view')\n if (denied) return denied\n const url = new URL(req.url)\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const limit = getQueryInt(url, 'limit', 50)\n if (limit > 500) return errorResponse(400, 'limit must not exceed 500')\n const filters: VisitorFilters = {\n tenantId: 'default',\n page: getQueryInt(url, 'page', 1),\n limit,\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n const visitors = await storage.getVisitors(filters)\n return json(200, visitors)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport type { UserService } from '../../services/user.service'\n\nexport function buildAdminUserRoutes(\n userService: UserService,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /users': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'user:view')\n if (denied) return denied\n const users = await userService.list()\n return json(200, users)\n }),\n\n 'GET /users/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'user:view')\n if (denied) return denied\n const user = await userService.get(p['id'] ?? '')\n if (!user) return errorResponse(404, 'User not found')\n const roles = await userService.getRoles(user.id)\n return json(200, { ...user, roles: roles.map(r => ({ id: r.id, name: r.name })) })\n }),\n\n 'POST /users': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'user:create')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || !b['name']) return errorResponse(400, 'name is required')\n if (typeof b['email'] !== 'string' || !b['email']) return errorResponse(400, 'email is required')\n if (typeof b['password'] !== 'string' || !b['password']) return errorResponse(400, 'password is required')\n if (b['password'].length < 12) return errorResponse(400, 'password must be at least 12 characters')\n const created = await userService.create(\n {\n name: b['name'],\n email: b['email'],\n password: b['password'],\n ...(typeof b['roleId'] === 'string' ? { roleId: b['roleId'] } : {}),\n },\n actor!.sub,\n )\n return json(201, created)\n }),\n\n 'PUT /users/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'user:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['password'] === 'string' && b['password'].length < 12) {\n return errorResponse(400, 'password must be at least 12 characters')\n }\n const updated = await userService.update(\n id,\n {\n ...(typeof b['name'] === 'string' ? { name: b['name'] } : {}),\n ...(typeof b['email'] === 'string' ? { email: b['email'] } : {}),\n ...(b['isActive'] != null ? { isActive: Boolean(b['isActive']) } : {}),\n ...(typeof b['password'] === 'string' ? { password: b['password'] } : {}),\n },\n actor!.sub,\n )\n return json(200, updated)\n }),\n\n 'DELETE /users/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'user:delete')\n if (denied) return denied\n const id = p['id'] ?? ''\n if (id === actor!.sub) return errorResponse(400, 'Cannot delete your own account')\n await userService.delete(id, actor!.sub)\n return json(200, { success: true })\n }),\n\n 'POST /users/:id/roles': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['roleId'] !== 'string') return errorResponse(400, 'roleId is required')\n const tenantId = typeof b['tenantId'] === 'string' ? b['tenantId'] : undefined\n await userService.assignRole(p['id'] ?? '', b['roleId'], tenantId)\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'user.role_assigned',\n resourceType: 'user',\n ...(p['id'] != null ? { resourceId: p['id'] } : {}),\n newData: { roleId: b['roleId'] },\n })\n return json(200, { success: true })\n }),\n\n 'DELETE /users/:id/roles/:roleId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:update')\n if (denied) return denied\n const url = new URL(req.url)\n const revokedTenantId = url.searchParams.get('tenantId') ?? undefined\n await userService.revokeRole(p['id'] ?? '', p['roleId'] ?? '', revokedTenantId)\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'user.role_revoked',\n resourceType: 'user',\n ...(p['id'] != null ? { resourceId: p['id'] } : {}),\n oldData: { roleId: p['roleId'] },\n })\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminRoleRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /roles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:view')\n if (denied) return denied\n const roles = await storage.getRoles('default')\n return json(200, roles)\n }),\n\n 'GET /roles/:id/permissions': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:view')\n if (denied) return denied\n const perms = await storage.getPermissionsForRole(p['id'] ?? '')\n return json(200, perms)\n }),\n\n 'POST /roles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:create')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || !b['name']) return errorResponse(400, 'name is required')\n const role = await storage.createRole({\n tenantId: 'default',\n name: b['name'],\n ...(typeof b['description'] === 'string' ? { description: b['description'] } : {}),\n })\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'role.created',\n resourceType: 'role',\n resourceId: role.id,\n newData: role,\n })\n return json(201, role)\n }),\n\n 'PUT /roles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n const role = await storage.updateRole(p['id'] ?? '', {\n ...(typeof b['name'] === 'string' ? { name: b['name'] } : {}),\n ...(typeof b['description'] === 'string' ? { description: b['description'] } : {}),\n })\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'role.updated',\n resourceType: 'role',\n resourceId: role.id,\n newData: role,\n })\n return json(200, role)\n }),\n\n 'DELETE /roles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:delete')\n if (denied) return denied\n await storage.deleteRole(p['id'] ?? '')\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'role.deleted',\n resourceType: 'role',\n ...(p['id'] != null ? { resourceId: p['id'] } : {}),\n })\n return json(200, { success: true })\n }),\n\n 'GET /permissions': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:view')\n if (denied) return denied\n const perms = await storage.getAllPermissions()\n return json(200, perms)\n }),\n\n 'POST /roles/:id/permissions': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['permissionId'] !== 'string') return errorResponse(400, 'permissionId is required')\n await storage.assignPermissionToRole(p['id'] ?? '', b['permissionId'])\n return json(200, { success: true })\n }),\n\n 'DELETE /roles/:id/permissions/:permissionId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:update')\n if (denied) return denied\n await storage.revokePermissionFromRole(p['id'] ?? '', p['permissionId'] ?? '')\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, AuditFilters } from '@consenti/types'\nimport { json, getQueryParam, getQueryInt } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminAuditRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, perm) }\n }\n\n return {\n 'GET /audit': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'audit:view')\n if (denied) return denied\n const url = new URL(req.url)\n const action = getQueryParam(url, 'action')\n const resourceType = getQueryParam(url, 'resourceType')\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const limit = getQueryInt(url, 'limit', 50)\n if (limit > 500) return errorResponse(400, 'limit must not exceed 500')\n const filters: AuditFilters = {\n tenantId: 'default',\n page: getQueryInt(url, 'page', 1),\n limit,\n ...(action !== undefined ? { action } : {}),\n ...(resourceType !== undefined ? { resourceType } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n const logs = await storage.getLogs(filters)\n return json(200, logs)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, getQueryInt } from '../../utils/http'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminStatsRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'consent:view') }\n }\n\n return {\n 'GET /stats/overview': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const stats = await storage.getOverviewStats('default')\n return json(200, stats)\n }),\n\n 'GET /stats/categories': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const stats = await storage.getCategoryStats('default')\n return json(200, stats)\n }),\n\n 'GET /stats/timeline': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const url = new URL(req.url)\n const days = getQueryInt(url, 'days', 30)\n const timeline = await storage.getTimeline('default', days)\n return json(200, timeline)\n }),\n\n 'GET /stats/countries': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const countries = await storage.getCountries('default')\n return json(200, countries)\n }),\n\n 'GET /stats/gpc': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const gpc = await storage.getGpcStats('default')\n return json(200, gpc)\n }),\n\n 'GET /stats/aggregate': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const tenants = await storage.getTenants()\n const rows = await Promise.all(\n tenants.map(async t => {\n const s = await storage.getOverviewStats(t.id)\n return { tenantId: t.id, tenantName: t.name, ...s }\n }),\n )\n const aggregate = rows.reduce(\n (acc, r) => ({\n totalConsents: acc.totalConsents + r.totalConsents,\n totalVisitors: acc.totalVisitors + r.totalVisitors,\n gpcDetectedCount: acc.gpcDetectedCount + r.gpcDetectedCount,\n }),\n { totalConsents: 0, totalVisitors: 0, gpcDetectedCount: 0 },\n )\n return json(200, { aggregate, perTenant: rows })\n }),\n }\n}\n","import type {\n StorageAdapter, AuthConfig, ConsentFilters, AuditFilters,\n ConsentDbRecord, AuditLog,\n} from '@consenti/types'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport { getQueryParam } from '../../utils/http'\n\nfunction escapeCsv(val: string | number | boolean | null | undefined): string {\n const s = val == null ? '' : String(val)\n if (s.includes(',') || s.includes('\"') || s.includes('\\n')) {\n return `\"${s.replace(/\"/g, '\"\"')}\"`\n }\n return s\n}\n\nfunction consentToCsvRow(r: ConsentDbRecord): string {\n return [\n r.id, r.visitorId, r.profileId, r.profileVersion, r.locale,\n JSON.stringify(r.consentJson), r.gpcDetected ? '1' : '0',\n r.source, r.createdAt, r.updatedAt,\n ].map(escapeCsv).join(',') + '\\n'\n}\n\nfunction auditToCsvRow(r: AuditLog): string {\n return [\n r.id, r.userId ?? '', r.action, r.resourceType, r.resourceId ?? '', r.createdAt,\n ].map(escapeCsv).join(',') + '\\n'\n}\n\nexport function buildAdminExportRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'export:run') }\n }\n\n return {\n 'GET /export/consents': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const url = new URL(req.url)\n const format = getQueryParam(url, 'format') ?? 'csv'\n const profileId = getQueryParam(url, 'profileId')\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const filters: ConsentFilters = {\n tenantId: 'default',\n ...(profileId !== undefined ? { profileId } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n\n if (format === 'json') {\n const records: ConsentDbRecord[] = []\n for await (const r of storage.streamConsents(filters)) records.push(r)\n return new Response(JSON.stringify(records), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Disposition': 'attachment; filename=\"consents.json\"',\n },\n })\n }\n\n const enc = new TextEncoder()\n const header = 'id,visitor_id,profile_id,profile_version,locale,consent_json,gpc_detected,source,created_at,updated_at\\n'\n const stream = new ReadableStream({\n async start(controller) {\n controller.enqueue(enc.encode(header))\n for await (const r of storage.streamConsents(filters)) {\n controller.enqueue(enc.encode(consentToCsvRow(r)))\n }\n controller.close()\n },\n })\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': 'attachment; filename=\"consents.csv\"',\n },\n })\n }),\n\n 'GET /export/consents/xlsx': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n\n interface XlsxModule {\n utils: {\n book_new(): object\n json_to_sheet(rows: object[]): object\n book_append_sheet(wb: object, ws: object, name: string): void\n }\n write(wb: object, opts: { type: string; bookType: string }): Uint8Array\n }\n let XlsxMod: XlsxModule | null = null\n try {\n // @ts-expect-error -- xlsx is an optional peer dependency; install it to use XLSX export\n XlsxMod = (await import('xlsx')) as XlsxModule\n } catch {\n return new Response(JSON.stringify({ error: 'xlsx peer dependency not installed. Run: npm install xlsx' }), {\n status: 501,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n if (!XlsxMod) return new Response(null, { status: 501 })\n\n const url = new URL(req.url)\n const profileId = url.searchParams.get('profileId') ?? undefined\n const from = url.searchParams.get('from') ?? undefined\n const to = url.searchParams.get('to') ?? undefined\n const filters: ConsentFilters = {\n tenantId: 'default',\n ...(profileId !== undefined ? { profileId } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n\n const rows: Record<string, unknown>[] = []\n for await (const r of storage.streamConsents(filters)) {\n rows.push({\n id: r.id,\n visitor_id: r.visitorId,\n profile_id: r.profileId,\n profile_version: r.profileVersion,\n locale: r.locale,\n consent_json: JSON.stringify(r.consentJson),\n gpc_detected: r.gpcDetected ? 1 : 0,\n age_verified: r.ageVerified ? 1 : 0,\n tcf_string: r.tcfString ?? '',\n source: r.source,\n created_at: r.createdAt,\n updated_at: r.updatedAt,\n })\n }\n\n const wb = XlsxMod.utils.book_new()\n const ws = XlsxMod.utils.json_to_sheet(rows)\n XlsxMod.utils.book_append_sheet(wb, ws, 'Consents')\n const buf = XlsxMod.write(wb, { type: 'buffer', bookType: 'xlsx' })\n return new Response(buf as unknown as BodyInit, {\n status: 200,\n headers: {\n 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'Content-Disposition': 'attachment; filename=\"consents.xlsx\"',\n },\n })\n }),\n\n 'GET /export/audit': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const url = new URL(req.url)\n const format = getQueryParam(url, 'format') ?? 'csv'\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const filters: AuditFilters = {\n tenantId: 'default',\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n\n if (format === 'json') {\n const logs: AuditLog[] = []\n for await (const r of storage.streamAuditLogs(filters)) logs.push(r)\n return new Response(JSON.stringify(logs), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Disposition': 'attachment; filename=\"audit.json\"',\n },\n })\n }\n\n const enc = new TextEncoder()\n const header = 'id,user_id,action,resource_type,resource_id,created_at\\n'\n const stream = new ReadableStream({\n async start(controller) {\n controller.enqueue(enc.encode(header))\n for await (const r of storage.streamAuditLogs(filters)) {\n controller.enqueue(enc.encode(auditToCsvRow(r)))\n }\n controller.close()\n },\n })\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': 'attachment; filename=\"audit.csv\"',\n },\n })\n }),\n }\n}\n","import { randomBytes, createHash } from 'node:crypto'\nimport type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json } from '../../utils/http'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nfunction generateApiKey(): { raw: string; hash: string } {\n const raw = `ck_live_${randomBytes(24).toString('hex')}`\n const hash = createHash('sha256').update(raw).digest('hex')\n return { raw, hash }\n}\n\nexport function buildAdminApiKeyRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, 'settings:update') }\n }\n\n return {\n 'GET /apikeys': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const keys = await storage.getApiKeys('default')\n return json(200, keys.map(k => ({ id: k.id, name: k.name, isActive: k.isActive, createdAt: k.createdAt })))\n }),\n\n 'POST /apikeys': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const body = await req.json() as { name?: string; tenantId?: string }\n if (!body.name) return json(400, { error: 'name is required' })\n const { raw, hash } = generateApiKey()\n const created = await storage.createApiKey({ tenantId: body.tenantId ?? 'default', name: body.name, keyHash: hash })\n return json(201, { id: created.id, name: created.name, key: raw, createdAt: created.createdAt })\n }),\n\n 'DELETE /apikeys/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n await storage.revokeApiKey(p['id'] ?? '')\n return json(204, null)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminTenantRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /tenants': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const tenants = await storage.getTenants()\n return json(200, tenants)\n }),\n\n 'POST /tenants': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || typeof b['slug'] !== 'string') {\n return errorResponse(400, 'name and slug are required')\n }\n const tenant = await storage.createTenant({ name: b['name'], slug: b['slug'] })\n return json(201, tenant)\n }),\n\n 'PUT /tenants/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n const tenant = await storage.updateTenant(p['id'] ?? '', {\n ...(typeof b['name'] === 'string' ? { name: b['name'] } : {}),\n ...(typeof b['slug'] === 'string' ? { slug: b['slug'] } : {}),\n })\n return json(200, tenant)\n }),\n\n 'DELETE /tenants/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n if (id === 'default') return errorResponse(400, 'Cannot delete the default tenant')\n await storage.deleteTenant(id)\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { getGvl } from '../../tcf/gvl-cache'\nimport { json } from '../../utils/http'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminTcfRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'consent:view') }\n }\n\n return {\n 'GET /tcf/vendors': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const gvl = await getGvl()\n if (!gvl) return json(503, { error: 'GVL not available. Enable tcf.enabled in config.' })\n const url = new URL(req.url)\n const search = url.searchParams.get('q')?.toLowerCase()\n const page = Math.max(1, parseInt(url.searchParams.get('page') ?? '1', 100))\n const limit = Math.min(200, Math.max(1, parseInt(url.searchParams.get('limit') ?? '100', 10)))\n const vendors = Object.values(gvl.vendors)\n const filtered = search\n ? vendors.filter(v => v.name.toLowerCase().includes(search))\n : vendors\n const total = filtered.length\n const totalPages = Math.max(1, Math.ceil(total / limit))\n const safePage = Math.min(page, totalPages)\n const start = (safePage - 1) * limit\n return json(200, {\n vendorListVersion: gvl.vendorListVersion,\n total,\n page: safePage,\n totalPages,\n vendors: filtered.slice(start, start + limit),\n })\n }),\n\n 'GET /tcf/purposes': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const gvl = await getGvl()\n if (!gvl) return json(503, { error: 'GVL not available. Enable tcf.enabled in config.' })\n return json(200, { purposes: Object.values(gvl.purposes) })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminCookieTemplateRoutes(storage: StorageAdapter, authConfig: AuthConfig, secret: string, tenantId = 'default') {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /cookie-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await storage.getCookieTemplates(tenantId))\n }),\n\n 'GET /cookie-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const t = await storage.getCookieTemplate(params['id']!)\n return t ? json(200, t) : errorResponse(404, 'Cookie template not found')\n }),\n\n 'POST /cookie-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown; cookies?: unknown }>(req)\n if (!body?.name || typeof body.name !== 'string') return errorResponse(400, 'name is required')\n if (!Array.isArray(body.cookies)) return errorResponse(400, 'cookies must be an array')\n const t = await storage.createCookieTemplate({ tenantId, name: body.name, cookies: body.cookies as never })\n return json(201, t)\n }),\n\n 'PUT /cookie-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown; cookies?: unknown }>(req)\n const update: { name?: string; cookies?: never } = {}\n if (body?.name !== undefined && typeof body.name === 'string') update.name = body.name\n if (body?.cookies !== undefined && Array.isArray(body.cookies)) update.cookies = body.cookies as never\n const t = await storage.updateCookieTemplate(params['id']!, update)\n return json(200, t)\n }),\n\n 'DELETE /cookie-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:delete')\n if (denied) return denied\n await storage.deleteCookieTemplate(params['id']!)\n return json(200, { ok: true })\n }),\n\n 'POST /cookie-templates/:id/copy': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown }>(req)\n const src = await storage.getCookieTemplate(params['id']!)\n if (!src) return errorResponse(404, 'Cookie template not found')\n const newName = (typeof body?.name === 'string' && body.name.trim()) ? body.name.trim() : `${src.name} (Copy)`\n const t = await storage.copyCookieTemplate(params['id']!, newName)\n return json(201, t)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminUITemplateRoutes(storage: StorageAdapter, authConfig: AuthConfig, secret: string, tenantId = 'default') {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /ui-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await storage.getUITemplates(tenantId))\n }),\n\n 'GET /ui-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const t = await storage.getUITemplate(params['id']!)\n return t ? json(200, t) : errorResponse(404, 'UI template not found')\n }),\n\n 'POST /ui-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<Record<string, unknown>>(req)\n if (!body?.name || typeof body.name !== 'string') return errorResponse(400, 'name is required')\n if (!body.mainBanner || !body.gpcBanner || !body.preferenceModal) return errorResponse(400, 'mainBanner, gpcBanner, and preferenceModal are required')\n const t = await storage.createUITemplate({\n tenantId,\n name: body.name,\n mainBanner: body.mainBanner as never,\n gpcBanner: body.gpcBanner as never,\n preferenceModal: body.preferenceModal as never,\n })\n return json(201, t)\n }),\n\n 'PUT /ui-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const body = await parseJsonBody<Record<string, unknown>>(req)\n const update: Record<string, unknown> = {}\n if (typeof body?.name === 'string') update['name'] = body.name\n if (body?.mainBanner) update['mainBanner'] = body.mainBanner\n if (body?.gpcBanner) update['gpcBanner'] = body.gpcBanner\n if (body?.preferenceModal) update['preferenceModal'] = body.preferenceModal\n const t = await storage.updateUITemplate(params['id']!, update as never)\n return json(200, t)\n }),\n\n 'DELETE /ui-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:delete')\n if (denied) return denied\n await storage.deleteUITemplate(params['id']!)\n return json(200, { ok: true })\n }),\n\n 'POST /ui-templates/:id/copy': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown }>(req)\n const src = await storage.getUITemplate(params['id']!)\n if (!src) return errorResponse(404, 'UI template not found')\n const newName = (typeof body?.name === 'string' && body.name.trim()) ? body.name.trim() : `${src.name} (Copy)`\n const t = await storage.copyUITemplate(params['id']!, newName)\n return json(201, t)\n }),\n }\n}\n","import { createHash } from 'node:crypto'\nimport type { StorageAdapter } from '@consenti/types'\n\nexport async function resolveTenant(\n req: Request,\n storage: StorageAdapter,\n defaultTenantId: string,\n): Promise<string> {\n const auth = req.headers.get('authorization')\n if (!auth?.startsWith('Bearer ck_')) return defaultTenantId\n\n const rawKey = auth.slice(7)\n const keyHash = createHash('sha256').update(rawKey).digest('hex')\n const apiKey = await storage.getApiKeyByHash(keyHash)\n return apiKey?.tenantId ?? defaultTenantId\n}\n","interface RateLimitEntry {\n count: number\n resetAt: number\n}\n\nexport class RateLimiter {\n private store = new Map<string, RateLimitEntry>()\n\n constructor(\n private windowMs: number = 60_000,\n private maxRequests: number = 60,\n ) {\n setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of this.store) {\n if (now >= entry.resetAt) this.store.delete(key)\n }\n }, this.windowMs).unref()\n }\n\n check(key: string): boolean {\n const now = Date.now()\n const entry = this.store.get(key)\n\n if (!entry || now >= entry.resetAt) {\n this.store.set(key, { count: 1, resetAt: now + this.windowMs })\n return true\n }\n\n entry.count += 1\n return entry.count <= this.maxRequests\n }\n\n // Returns false if either the IP bucket or the profileId bucket is exhausted.\n checkRequest(ip: string, profileId?: string): boolean {\n const ipOk = this.check(`ip:${ip}`)\n if (!ipOk) return false\n if (profileId) return this.check(`profile:${profileId}`)\n return true\n }\n}\n"],"mappings":"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","names":["readFile","dirname","MAX_PAGE_SIZE","MAX_PAGE_SIZE","q","page","visitors","mapProfile","mapConsent","mapHistory","mapVisitor","mapUser","mapRole","mapPermission","mapAuditLog","page","visitors","mapProfile","mapConsent","mapHistory","mapVisitor","mapUser","mapRole","mapPermission","mapAuditLog","page","visitors","rows","row","randomUUID","MAX_PAGE_SIZE","randomProfileId","randomUUID","c","v","c","profiles","status","visitors","profiles","visitors","roles","permissions","profiles","visitors","profiles","randomBytes","roles","permissions","randomBytes","createHash","roles","createHmac","randomBytes","timingSafeEqual","timingSafeEqual","base64url","roles","randomBytes","permissions","visitors","roles","roles","tenants","randomBytes","createHash","randomBytes","createHash","tenants","page","createHash","dirname","dashboardDir","readFile"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/plugin-engine.ts","../src/utils/handle-config.ts","../src/tcf/gvl-cache.ts","../src/openapi/spec.ts","../src/utils/crypto.ts","../src/utils/storage-path.ts","../src/storage/seed-data.ts","../src/utils/sql-search.ts","../src/storage/sqlite/better-sqlite3.adapter.ts","../src/storage/sqlite/node-sqlite3-wasm.adapter.ts","../src/storage/sqlite/node-sqlite-builtin.adapter.ts","../src/storage/mongodb/mongodb.adapter.ts","../src/storage/mysql/mysql.adapter.ts","../src/storage/postgresql/postgresql.adapter.ts","../src/storage/json/json-file.adapter.ts","../src/repositories/profile.repo.ts","../src/repositories/consent.repo.ts","../src/repositories/visitor.repo.ts","../src/repositories/notice.repo.ts","../src/repositories/audit.repo.ts","../src/utils/ttl-cache.ts","../src/services/stats.service.ts","../src/repositories/user.repo.ts","../src/services/profile.service.ts","../src/workers/profile-json-worker-launcher.ts","../src/workers/profile-json-worker-core.ts","../src/storage/s3/s3-client.ts","../src/data/timezone-country.ts","../src/services/geo-resolver.service.ts","../src/services/locale-json-cache.service.ts","../src/utils/safe-json-write.ts","../src/core/consent-engine.ts","../src/services/consent.service.ts","../src/services/visitor.service.ts","../src/services/notice.service.ts","../src/services/user.service.ts","../src/auth/local.auth.ts","../src/utils/http.ts","../src/middleware/error.middleware.ts","../src/routes/public/profile.routes.ts","../src/utils/cookie.ts","../src/middleware/cors.middleware.ts","../src/validators/consent.validator.ts","../src/routes/public/consent.routes.ts","../src/routes/public/notice.routes.ts","../src/routes/admin/auth.routes.ts","../src/middleware/auth.middleware.ts","../src/auth/oidc.auth.ts","../src/auth/saml.auth.ts","../src/auth/totp.ts","../src/validators/profile.validator.ts","../src/services/compliance-validator.service.ts","../src/services/profile-content-validator.service.ts","../src/routes/admin/profiles.routes.ts","../src/routes/admin/consents.routes.ts","../src/routes/admin/visitors.routes.ts","../src/routes/admin/users.routes.ts","../src/routes/admin/roles.routes.ts","../src/routes/admin/audit.routes.ts","../src/routes/admin/stats.routes.ts","../src/routes/admin/export.routes.ts","../src/routes/admin/apikeys.routes.ts","../src/routes/admin/tenants.routes.ts","../src/routes/admin/settings.routes.ts","../src/routes/admin/tcf.routes.ts","../src/validators/consent-template.validator.ts","../src/routes/admin/consent-templates.routes.ts","../src/routes/admin/ui-templates.routes.ts","../src/routes/admin/analytics.routes.ts","../src/utils/sanitize-config.ts","../src/routes/admin/setup.routes.ts","../src/middleware/tenant.middleware.ts","../src/middleware/rate-limit.middleware.ts","../src/hooks/consent-action.ts","../src/hooks/category-action.ts"],"sourcesContent":["import { EventEmitter } from 'node:events'\nimport { readFile } from 'node:fs/promises'\nimport { join, extname, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { PluginEngine } from './core/plugin-engine'\nimport { buildConsentiRuntimeConfig, buildConsentiConfigScript, injectConfigScript } from './utils/handle-config'\nimport { startGvlRefresh, stopGvlRefresh } from './tcf/gvl-cache'\nimport { OPENAPI_PUBLIC_SPEC, OPENAPI_ADMIN_SPEC } from './openapi/spec'\nimport { BetterSqlite3Adapter } from './storage/sqlite/better-sqlite3.adapter'\nimport { NodeSqlite3WasmAdapter } from './storage/sqlite/node-sqlite3-wasm.adapter'\nimport { NodeSqliteBuiltinAdapter } from './storage/sqlite/node-sqlite-builtin.adapter'\nimport { MongoDBAdapter } from './storage/mongodb/mongodb.adapter'\nimport { MySQLAdapter } from './storage/mysql/mysql.adapter'\nimport { PostgreSQLAdapter } from './storage/postgresql/postgresql.adapter'\nimport { JsonFileAdapter } from './storage/json/json-file.adapter'\nimport { ProfileRepo } from './repositories/profile.repo'\nimport { ConsentRepo } from './repositories/consent.repo'\nimport { VisitorRepo } from './repositories/visitor.repo'\nimport { NoticeRepo } from './repositories/notice.repo'\nimport { AuditRepo } from './repositories/audit.repo'\nimport { StatsService } from './services/stats.service'\nimport { UserRepo } from './repositories/user.repo'\nimport { ProfileService } from './services/profile.service'\nimport { GeoResolverService } from './services/geo-resolver.service'\nimport { LocaleJsonCacheService } from './services/locale-json-cache.service'\nimport { ConsentService } from './services/consent.service'\nimport { VisitorService } from './services/visitor.service'\nimport { NoticeService } from './services/notice.service'\nimport { UserService } from './services/user.service'\nimport { LocalAuth } from './auth/local.auth'\nimport { buildProfileRoutes } from './routes/public/profile.routes'\nimport { buildConsentRoutes } from './routes/public/consent.routes'\nimport { buildNoticeRoutes } from './routes/public/notice.routes'\nimport { buildAdminAuthRoutes } from './routes/admin/auth.routes'\nimport { buildAdminProfileRoutes } from './routes/admin/profiles.routes'\nimport { buildAdminConsentRoutes } from './routes/admin/consents.routes'\nimport { buildAdminVisitorRoutes } from './routes/admin/visitors.routes'\nimport { buildAdminUserRoutes } from './routes/admin/users.routes'\nimport { buildAdminRoleRoutes } from './routes/admin/roles.routes'\nimport { buildAdminAuditRoutes } from './routes/admin/audit.routes'\nimport { buildAdminStatsRoutes } from './routes/admin/stats.routes'\nimport { buildAdminExportRoutes } from './routes/admin/export.routes'\nimport { buildAdminApiKeyRoutes } from './routes/admin/apikeys.routes'\nimport { buildAdminTenantRoutes } from './routes/admin/tenants.routes'\nimport { buildAdminSettingsRoutes } from './routes/admin/settings.routes'\nimport { buildAdminTcfRoutes } from './routes/admin/tcf.routes'\nimport { buildAdminConsentTemplateRoutes } from './routes/admin/consent-templates.routes'\nimport { buildAdminUITemplateRoutes } from './routes/admin/ui-templates.routes'\nimport { buildAdminAnalyticsRoutes } from './routes/admin/analytics.routes'\nimport { buildAdminSetupRoutes } from './routes/admin/setup.routes'\nimport { resolveStoragePaths } from './utils/storage-path'\nimport { resolveTenant } from './middleware/tenant.middleware'\nimport { RateLimiter } from './middleware/rate-limit.middleware'\nimport { handleCors, withCors, addSecurityHeaders, checkOriginAllowed } from './middleware/cors.middleware'\nimport { errorResponse } from './middleware/error.middleware'\nimport { nodeRequestToFetch, fetchResponseToNode, PayloadTooLargeError } from './utils/http'\nimport type { ConsentiServerConfig, StorageAdapter, StorageConfig } from '@consenti/types'\nimport { EMBEDDED_COMPLIANCE_MAP } from '@consenti/utils'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\n\n// ── Path resolution ────────────────────────────────────────────────────────────\n\nconst DIST_DIR = dirname(fileURLToPath(import.meta.url))\n// new URL pattern is recognised by @vercel/nft (Next.js standalone tracer),\n// causing it to include the dashboard/ directory for any consumer's standalone build.\nexport const dashboardDir: string = fileURLToPath(new URL('./dashboard', import.meta.url))\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n '.ttf': 'font/ttf',\n}\n\nasync function tryServeDashboard(\n pathname: string,\n basePath: string,\n dashboardDir: string,\n res: ServerResponse,\n configScript: string,\n): Promise<boolean> {\n const rel = pathname.slice(basePath.length) || '/'\n if (rel.startsWith('/api/') || rel.startsWith('/admin/')) return false\n const target = rel === '/' || rel === '' || !extname(rel)\n ? join(dashboardDir, 'index.html')\n : join(dashboardDir, rel)\n // directory traversal guard\n if (!target.startsWith(dashboardDir)) return false\n try {\n const raw = await readFile(target)\n const ext = extname(target).toLowerCase()\n const isHtml = ext === '.html' || ext === ''\n const ct = MIME[ext] ?? 'application/octet-stream'\n res.writeHead(200, { 'Content-Type': ct })\n res.end(isHtml ? injectConfigScript(raw, configScript) : raw)\n return true\n } catch {\n return false\n }\n}\n\nasync function serveDistFile(filePath: string, contentType: string): Promise<Response> {\n try {\n const data = await readFile(filePath)\n return new Response(data, { headers: { 'Content-Type': contentType, 'Cache-Control': 'public, max-age=3600' } })\n } catch {\n return new Response('Not Found', { status: 404 })\n }\n}\n\n// ── Minimal path router ────────────────────────────────────────────────────────\n\ntype Handler = (req: Request, params: Record<string, string>) => Promise<Response>\n\ninterface RouteEntry {\n method: string\n pattern: RegExp\n paramNames: string[]\n handler: Handler\n rateLimit: boolean\n /** Enforce `TenantSettings.adminAllowedOrigins` as a CORS-layer check on top of this route's\n * own auth. Only ever set on the authenticated admin API group — not on unauthenticated\n * static assets like `widget.js`/`widget.css`, which must stay embeddable from any origin. */\n originCheck: boolean\n}\n\nfunction compilePath(path: string): { pattern: RegExp; paramNames: string[] } {\n const paramNames: string[] = []\n const regexParts = path.split('/').map(part => {\n if (part.startsWith(':')) {\n paramNames.push(part.slice(1))\n return '([^/]+)'\n }\n return part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n })\n return { pattern: new RegExp(`^${regexParts.join('\\\\/')}$`), paramNames }\n}\n\ninterface RouteGroup {\n prefix: string\n routes: Record<string, Handler>\n rateLimit?: boolean\n originCheck?: boolean\n}\n\nfunction branded404(basePath: string): Response {\n const docsHref = `${basePath}/api/docs`\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>404 — Consenti</title>\n<style>\n *{box-sizing:border-box;margin:0;padding:0}\n body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0f1117;color:#e2e8f0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:2rem}\n .card{text-align:center;max-width:480px;width:100%}\n .badge{display:inline-flex;align-items:center;gap:.5rem;background:#1e2330;border:1px solid #2d3347;border-radius:999px;padding:.35rem .85rem;font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;color:#7c8db5;margin-bottom:2rem}\n .badge svg{width:14px;height:14px;fill:#4f6ef7}\n h1{font-size:6rem;font-weight:800;line-height:1;background:linear-gradient(135deg,#4f6ef7,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:.5rem}\n h2{font-size:1.25rem;font-weight:600;color:#cbd5e1;margin-bottom:.75rem}\n p{font-size:.9rem;color:#64748b;line-height:1.6;margin-bottom:2rem}\n a{display:inline-flex;align-items:center;gap:.4rem;background:#4f6ef7;color:#fff;text-decoration:none;padding:.6rem 1.25rem;border-radius:.5rem;font-size:.875rem;font-weight:500;transition:background .15s}\n a:hover{background:#3b5ce6}\n a svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}\n</style>\n</head>\n<body>\n<div class=\"card\">\n <div class=\"badge\">\n <svg viewBox=\"0 0 16 16\"><path d=\"M8 1a7 7 0 100 14A7 7 0 008 1zm0 3a1 1 0 110 2 1 1 0 010-2zm1 7H7V7h2v4z\"/></svg>\n Consenti\n </div>\n <h1>404</h1>\n <h2>Page not found</h2>\n <p>The admin dashboard is not enabled on this instance.<br>Use the API or enable <code style=\"background:#1e2330;padding:.1rem .35rem;border-radius:.25rem;font-size:.8rem\">dashboard: true</code> in your config.</p>\n <a href=\"${docsHref}\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\"/><line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\"/><polyline points=\"10 9 9 9 8 9\"/></svg>\n View API Documentation\n </a>\n</div>\n</body>\n</html>`\n return new Response(html, { status: 404, headers: { 'Content-Type': 'text/html; charset=utf-8' } })\n}\n\nfunction buildFetchRouter(\n basePath: string,\n groups: RouteGroup[],\n rateLimiter: RateLimiter | null,\n storage: StorageAdapter,\n resolveTenantId: (req: Request) => Promise<string>,\n): (request: Request) => Promise<Response> {\n const entries: RouteEntry[] = []\n\n for (const group of groups) {\n for (const [key, handler] of Object.entries(group.routes)) {\n const spaceIdx = key.indexOf(' ')\n const method = key.slice(0, spaceIdx).toUpperCase()\n const path = key.slice(spaceIdx + 1)\n const { pattern, paramNames } = compilePath(`${group.prefix}${path}`)\n entries.push({\n method, pattern, paramNames, handler,\n rateLimit: group.rateLimit ?? false,\n originCheck: group.originCheck ?? false,\n })\n }\n }\n\n return async (request: Request): Promise<Response> => {\n const corsResponse = handleCors(request)\n if (corsResponse) return corsResponse\n\n const url = new URL(request.url)\n const pathname = url.pathname\n const method = request.method.toUpperCase()\n\n if (rateLimiter) {\n const matched = entries.find(e => e.rateLimit && pathname.match(e.pattern))\n if (matched) {\n const ip = request.headers.get('x-real-ip') ?? ''\n if (!rateLimiter.checkRequest(ip)) {\n return withCors(errorResponse(429, 'Too Many Requests'), request)\n }\n }\n }\n\n for (const entry of entries) {\n if (entry.method !== method) continue\n const match = pathname.match(entry.pattern)\n if (!match) continue\n // Origin-layer check on top of the route's own Bearer-token auth — only ever set for\n // the authenticated admin API group. No-ops for non-browser callers (no Origin header)\n // and whenever adminAllowedOrigins is unset, matching checkOriginAllowed's own defaults.\n if (entry.originCheck) {\n const tenantId = await resolveTenantId(request)\n const { adminAllowedOrigins } = await storage.getSettings(tenantId)\n if (!checkOriginAllowed(request, adminAllowedOrigins ?? [])) {\n return withCors(errorResponse(403, 'Origin not allowed for the admin API'), request)\n }\n }\n const params: Record<string, string> = {}\n entry.paramNames.forEach((name, i) => {\n const val = match[i + 1]\n if (val != null) params[name] = val\n })\n const response = await entry.handler(request, params)\n return addSecurityHeaders(withCors(response, request))\n }\n\n const isUiPath =\n pathname.startsWith(basePath) &&\n !pathname.startsWith(`${basePath}/api/`) &&\n !pathname.startsWith(`${basePath}/admin/`)\n\n return addSecurityHeaders(\n withCors(isUiPath ? branded404(basePath) : errorResponse(404, 'Not Found'), request),\n )\n }\n}\n\n// ── Deep merge ────────────────────────────────────────────────────────────────\n\nfunction isPlainObj(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base }\n for (const key of Object.keys(override)) {\n const ov = override[key]\n const bv = result[key]\n if (ov !== undefined && isPlainObj(ov) && isPlainObj(bv)) {\n result[key] = deepMerge(bv, ov)\n } else if (ov !== undefined) {\n result[key] = ov\n }\n }\n return result\n}\n\n// ── Default config ────────────────────────────────────────────────────────────\n\nconst DEFAULT_CONFIG: ConsentiServerConfig = {\n dashboard: true,\n basePath: process.env['CONSENTI_BASE_PATH'] ?? '/consenti',\n ...(process.env['CONSENTI_MAX_BODY_SIZE'] ? { maxBodySize: Number(process.env['CONSENTI_MAX_BODY_SIZE']) } : {}),\n storage: {\n driver: (process.env['CONSENTI_DB_DRIVER'] as StorageConfig['driver'] | undefined) ?? 'json',\n path: process.env['CONSENTI_DB_PATH'] ?? './consenti-data.json',\n ...(process.env['CONSENTI_DB_URI'] ? { uri: process.env['CONSENTI_DB_URI'] } : {}),\n ...(process.env['CONSENTI_DB_DATABASE'] ? { database: process.env['CONSENTI_DB_DATABASE'] } : {}),\n ...(process.env['CONSENTI_DB_HOST'] ? { host: process.env['CONSENTI_DB_HOST'] } : {}),\n ...(process.env['CONSENTI_DB_PORT'] ? { port: Number(process.env['CONSENTI_DB_PORT']) } : {}),\n ...(process.env['CONSENTI_DB_USER'] ? { user: process.env['CONSENTI_DB_USER'] } : {}),\n ...(process.env['CONSENTI_DB_PASSWORD'] ? { password: process.env['CONSENTI_DB_PASSWORD'] } : {}),\n },\n auth: {\n mode: 'local',\n adminEmail: process.env['CONSENTI_ADMIN_EMAIL'] ?? 'user@consenti.dev',\n adminPassword: process.env['CONSENTI_ADMIN_PASSWORD'] ?? 'Consenti@123',\n ...(process.env['CONSENTI_ADMIN_JWT_SECRET'] ? { jwtSecret: process.env['CONSENTI_ADMIN_JWT_SECRET'] } : {}),\n },\n rateLimit: {\n windowMs: process.env['CONSENTI_RATE_LIMIT_WINDOW_MS'] ? Number(process.env['CONSENTI_RATE_LIMIT_WINDOW_MS']) : 60_000,\n maxRequests: process.env['CONSENTI_RATE_LIMIT_MAX_REQUESTS'] ? Number(process.env['CONSENTI_RATE_LIMIT_MAX_REQUESTS']) : 60,\n },\n branding: {\n appLogoPath: \"./logo-dark.svg\",\n appName: \"Consenti\",\n hidePoweredBy: false,\n }\n}\n\n// ── Factory ────────────────────────────────────────────────────────────────────\n\nfunction createStorageAdapter(config: NonNullable<ConsentiServerConfig['storage']>): StorageAdapter {\n switch (config.driver) {\n case 'mongodb': return new MongoDBAdapter(config)\n case 'mysql': return new MySQLAdapter(config)\n case 'postgresql': return new PostgreSQLAdapter(config)\n case 'node:sqlite': return new NodeSqliteBuiltinAdapter(config)\n case 'better-sqlite3':\n case 'sqlite': return new BetterSqlite3Adapter(config)\n case 'node-sqlite3-wasm': return new NodeSqlite3WasmAdapter(config)\n case 'json':\n default: return new JsonFileAdapter(config)\n }\n}\n\nexport function createConsenti(userConfig: ConsentiServerConfig) {\n const config = deepMerge(\n DEFAULT_CONFIG as unknown as Record<string, unknown>,\n userConfig as unknown as Record<string, unknown>,\n ) as ConsentiServerConfig\n\n // Also surfaced (not just logged) on the setup wizard's confirmation step — see setup.routes.ts.\n const usingJsonStorage = config.storage?.driver === 'json'\n const usingDefaultCredentials = !userConfig.auth && !process.env['CONSENTI_ADMIN_PASSWORD']\n\n if (usingJsonStorage) {\n console.warn('[Consenti] Using the JSON file storage driver — fine for development, but switch to a database driver (node:sqlite, postgresql, mysql, mongodb) for production. Set storage.driver or CONSENTI_DB_DRIVER.')\n }\n if (usingDefaultCredentials) {\n console.warn('[Consenti] No auth config or CONSENTI_ADMIN_PASSWORD env var — using demo credentials. Set CONSENTI_ADMIN_EMAIL and CONSENTI_ADMIN_PASSWORD before going to production.')\n }\n\n const storageConfig = config.storage!\n const authConfig = config.auth!\n\n const storagePaths = resolveStoragePaths(\n storageConfig.path ?? './consenti-data',\n storageConfig.driver ?? 'json',\n )\n const profilesDir = storagePaths.profilesDir\n\n const storage = createStorageAdapter(storageConfig)\n const eventBus = new EventEmitter()\n\n const profileRepo = new ProfileRepo(storage)\n const consentRepo = new ConsentRepo(storage)\n const visitorRepo = new VisitorRepo(storage)\n const noticeRepo = new NoticeRepo(storage)\n const auditRepo = new AuditRepo(storage)\n const userRepo = new UserRepo(storage)\n const statsService = new StatsService(storage)\n\n const pluginEngine = new PluginEngine()\n if (config.plugins?.length) pluginEngine.register(config.plugins)\n\n const compliance = config.compliance ?? {}\n const complianceType = compliance.type ?? 'auto'\n const needsGeo = complianceType === 'auto'\n\n const localeCache = (config as { localeJsonCache?: { enabled?: boolean; dir?: string } }).localeJsonCache?.enabled\n ? new LocaleJsonCacheService((config as { localeJsonCache?: { dir?: string } }).localeJsonCache!.dir!)\n : undefined\n\n const geoResolver = needsGeo\n ? new GeoResolverService(compliance.geoDataProvider ?? 'default', EMBEDDED_COMPLIANCE_MAP)\n : undefined\n\n if (complianceType === 'auto' && !compliance.geoDataProvider) {\n console.info(\"[Consenti] compliance.geoDataProvider not set — defaulting to timezone+language heuristic\")\n }\n if (complianceType !== 'auto' && compliance.geoDataProvider) {\n console.warn('[Consenti] compliance.geoDataProvider is ignored when compliance.type is a fixed group')\n }\n\n const profileService = new ProfileService(profileRepo, auditRepo, 'default', storage, eventBus, localeCache, profilesDir, config.handleCache, config.s3Api)\n const visitorService = new VisitorService(visitorRepo, 'default', eventBus)\n const noticeService = new NoticeService(noticeRepo, 'default')\n const consentService = new ConsentService(\n consentRepo, visitorRepo, profileRepo, auditRepo, 'default', config.compliance,\n pluginEngine, eventBus, config.tcf, storage, config.consentSigningKey,\n )\n const userService = new UserService(userRepo, auditRepo)\n\n const adminSecret = authConfig.jwtSecret ?? ''\n const localAuth = new LocalAuth(storage, adminSecret)\n\n const rl = config.rateLimit!\n const rateLimiter = rl.enabled === false\n ? null\n : new RateLimiter(rl.windowMs!, rl.maxRequests!)\n const authLimiter = new RateLimiter(15 * 60_000, 10)\n\n const multiTenantEnabled = config.multiTenant?.enabled === true\n const resolveTenantId = multiTenantEnabled\n ? (req: Request) => resolveTenant(req, storage, 'default')\n : async (_req: Request) => 'default'\n\n const basePath = config.basePath!\n const apiBase = `${basePath}/api/v1`\n const adminBase = `${basePath}/admin`\n const docsPath = `${basePath}/api`\n\n const dashboardEnabled = !!config.dashboard\n const dashboardDir = join(DIST_DIR, 'dashboard')\n\n const configScript = dashboardEnabled\n ? buildConsentiConfigScript(\n buildConsentiRuntimeConfig(dashboardDir, basePath, config.branding, config.compliance, authConfig.mode),\n )\n : ''\n\n const swaggerHtml = `<!DOCTYPE html><html><head><title>Consenti API Docs</title>\n<link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5.17.14/swagger-ui.css\" crossorigin=\"anonymous\"></head>\n<body><div id=\"swagger-ui\"></div>\n<script src=\"https://unpkg.com/swagger-ui-dist@5.17.14/swagger-ui-bundle.js\" crossorigin=\"anonymous\"></script>\n<script src=\"https://unpkg.com/swagger-ui-dist@5.17.14/swagger-ui-standalone-preset.js\" crossorigin=\"anonymous\"></script>\n<script>SwaggerUIBundle({urls:[{url:'${docsPath}/openapi.json',name:'Public API (/consenti/api)'},{url:'${docsPath}/openapi-admin.json',name:'Admin API (/consenti/admin)'}],'urls.primaryName':'Public API (/consenti/api)',dom_id:'#swagger-ui',presets:[SwaggerUIBundle.presets.apis,SwaggerUIStandalonePreset],layout:'StandaloneLayout'})</script>\n</body></html>`\n\n const robotsTxt = `User-agent: *\\nDisallow: ${basePath}/\\n`\n\n const fetchRouter = buildFetchRouter(\n basePath,\n [\n {\n prefix: docsPath,\n rateLimit: false,\n routes: {\n 'GET /docs': async () => new Response(swaggerHtml, { headers: { 'Content-Type': 'text/html; charset=utf-8' } }),\n 'GET /openapi.json': async () => new Response(JSON.stringify(OPENAPI_PUBLIC_SPEC, null, 2), { headers: { 'Content-Type': 'application/json' } }),\n 'GET /openapi-admin.json': async () => new Response(JSON.stringify(OPENAPI_ADMIN_SPEC, null, 2), { headers: { 'Content-Type': 'application/json' } }),\n },\n },\n {\n prefix: '',\n rateLimit: false,\n routes: {\n 'GET /robots.txt': async () => new Response(robotsTxt, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }),\n },\n },\n {\n prefix: apiBase,\n rateLimit: true,\n routes: {\n ...buildProfileRoutes(profileService, geoResolver, profilesDir, apiBase),\n ...buildConsentRoutes(consentService, visitorService, profileService, resolveTenantId, storage, adminSecret),\n ...buildNoticeRoutes(noticeService),\n },\n },\n {\n // Unauthenticated static assets — must stay embeddable/fetchable from any origin\n // (customer sites load the widget script cross-origin), so no originCheck here even\n // though they share the adminBase prefix with the real, authenticated admin API below.\n prefix: adminBase,\n rateLimit: false,\n routes: {\n 'GET /widget.js': async () => serveDistFile(join(DIST_DIR, 'widget', 'widget.js'), 'application/javascript; charset=utf-8'),\n 'GET /widget.css': async () => serveDistFile(join(DIST_DIR, 'widget', 'widget.css'), 'text/css; charset=utf-8'),\n },\n },\n {\n prefix: adminBase,\n rateLimit: false,\n originCheck: true,\n routes: {\n ...buildAdminAuthRoutes(localAuth, storage, authConfig, adminSecret, authLimiter),\n ...buildAdminProfileRoutes(profileService, storage, authConfig, adminSecret),\n ...buildAdminConsentRoutes(storage, authConfig, adminSecret),\n ...buildAdminVisitorRoutes(storage, authConfig, adminSecret),\n ...buildAdminUserRoutes(userService, storage, authConfig, adminSecret),\n ...buildAdminRoleRoutes(storage, authConfig, adminSecret),\n ...buildAdminAuditRoutes(storage, authConfig, adminSecret),\n ...buildAdminStatsRoutes(storage, statsService, authConfig, adminSecret),\n ...buildAdminExportRoutes(storage, authConfig, adminSecret, profileService),\n ...buildAdminApiKeyRoutes(storage, authConfig, adminSecret),\n ...buildAdminTenantRoutes(storage, authConfig, adminSecret),\n ...buildAdminSettingsRoutes(storage, authConfig, adminSecret),\n ...buildAdminTcfRoutes(storage, authConfig, adminSecret),\n ...buildAdminConsentTemplateRoutes(storage, authConfig, adminSecret),\n ...buildAdminUITemplateRoutes(storage, authConfig, adminSecret),\n ...buildAdminAnalyticsRoutes(storage, authConfig, adminSecret),\n ...buildAdminSetupRoutes(profileService, storage, authConfig, adminSecret, config, { usingJsonStorage, usingDefaultCredentials }),\n },\n },\n ],\n rateLimiter,\n storage,\n resolveTenantId,\n )\n\n let retentionTimer: ReturnType<typeof setInterval> | null = null\n let auditRetentionTimer: ReturnType<typeof setInterval> | null = null\n\n // Resolves after storage.connect() + bootstrap() complete — consumers can await\n // this before accepting requests to guarantee the admin user already exists.\n let resolveReady!: () => void\n let rejectReady!: (err: unknown) => void\n const ready = new Promise<void>((res, rej) => { resolveReady = res; rejectReady = rej })\n\n storage\n .connect()\n .then(async () => {\n if (authConfig.mode === 'local' && authConfig.adminEmail && authConfig.adminPassword) {\n await localAuth.bootstrap(authConfig.adminEmail, authConfig.adminPassword)\n }\n await pluginEngine.initialize({ storage, config })\n eventBus.emit('ready')\n resolveReady()\n\n if (config.tcf?.enabled) {\n startGvlRefresh()\n }\n\n if (config.dataRetention?.purgeAfterDays) {\n const days = config.dataRetention.purgeAfterDays\n const purge = () => storage.purgeExpiredConsents(days)\n .then(n => { if (n > 0) console.warn(`[consenti] Purged ${n} consent records older than ${days} days`) })\n .catch((err: unknown) => console.warn('[consenti] Data retention purge failed:', err))\n void purge()\n retentionTimer = setInterval(() => { void purge() }, 24 * 60 * 60_000)\n }\n\n if (config.dataRetention?.auditLogPurgeAfterDays) {\n const days = config.dataRetention.auditLogPurgeAfterDays\n const purgeAudit = () => storage.purgeExpiredAuditLogs(days)\n .then(n => { if (n > 0) console.warn(`[consenti] Purged ${n} audit log entries older than ${days} days`) })\n .catch((err: unknown) => console.warn('[consenti] Audit log retention purge failed:', err))\n void purgeAudit()\n auditRetentionTimer = setInterval(() => { void purgeAudit() }, 24 * 60 * 60_000)\n }\n })\n .catch((err: unknown) => {\n console.warn('[consenti] Storage connection failed:', err)\n rejectReady(err)\n })\n\n async function nodeHandler(req: IncomingMessage, res: ServerResponse): Promise<void> {\n try {\n const rawProto = req.headers['x-forwarded-proto']\n const proto = rawProto === 'https' ? 'https' : 'http'\n const host = req.headers['host'] ?? 'localhost'\n const url = new URL(req.url ?? '/', `${proto}://${host}`)\n\n if (dashboardEnabled && url.pathname.startsWith(basePath)) {\n const served = await tryServeDashboard(url.pathname, basePath, dashboardDir, res, configScript)\n if (served) return\n }\n\n const request = await nodeRequestToFetch(req, config.maxBodySize, config.trustedProxies)\n const response = await fetchRouter(request)\n\n if (url.pathname === `${basePath}/api/openapi.json`) {\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(OPENAPI_PUBLIC_SPEC, null, 2))\n return\n }\n\n if (url.pathname === `${basePath}/api/openapi-admin.json`) {\n res.writeHead(200, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify(OPENAPI_ADMIN_SPEC, null, 2))\n return\n }\n\n if (url.pathname === `${basePath}/api/docs`) {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(swaggerHtml)\n return\n }\n\n if (\n response.status === 404 &&\n dashboardEnabled &&\n url.pathname.startsWith(basePath) &&\n !url.pathname.startsWith(basePath + '/api/') &&\n !url.pathname.startsWith(basePath + '/admin/')\n ) {\n try {\n const html = await readFile(join(dashboardDir, 'index.html'))\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(injectConfigScript(html, configScript))\n return\n } catch {\n // dashboard not built — fall through\n }\n }\n\n await fetchResponseToNode(response, res)\n } catch (err) {\n if (!res.headersSent) {\n if (err instanceof PayloadTooLargeError) {\n res.writeHead(413, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Payload Too Large' }))\n } else {\n res.writeHead(500, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ error: 'Internal Server Error' }))\n }\n }\n }\n }\n\n function router(\n req: IncomingMessage,\n res: ServerResponse,\n next: (err?: unknown) => void,\n ): void {\n nodeHandler(req, res)\n .then(() => { if (!res.writableEnded) next() })\n .catch(next)\n }\n\n function handler(req: IncomingMessage, res: ServerResponse): Promise<void> {\n return nodeHandler(req, res)\n }\n\n const fastifyHandler = async (\n fastify: {\n all: (\n path: string,\n handler: (req: { raw: IncomingMessage }, reply: { raw: ServerResponse; hijack: () => void }) => Promise<void>,\n ) => void\n },\n _opts: unknown,\n ): Promise<void> => {\n fastify.all('/*', async (request, reply) => {\n await nodeHandler(request.raw, reply.raw)\n reply.hijack()\n })\n }\n Object.defineProperty(fastifyHandler, Symbol.for('skip-override'), { value: true })\n\n const fetchHandler = async (request: Request): Promise<Response> => {\n if (dashboardEnabled) {\n const url = new URL(request.url)\n const pathname = url.pathname\n if (pathname.startsWith(basePath)) {\n const rel = pathname.slice(basePath.length) || '/'\n if (!rel.startsWith('/api/') && !rel.startsWith('/admin/')) {\n const target = extname(rel) ? join(dashboardDir, rel) : join(dashboardDir, 'index.html')\n if (!target.startsWith(dashboardDir)) {\n return new Response('Forbidden', { status: 403 })\n }\n try {\n const raw = await readFile(target)\n const ext = extname(target).toLowerCase()\n const isHtml = ext === '.html' || ext === ''\n const ct = MIME[ext] ?? 'application/octet-stream'\n const body = isHtml ? injectConfigScript(raw, configScript) : raw\n return new Response(body, { headers: { 'Content-Type': ct } })\n } catch {\n try {\n const html = await readFile(join(dashboardDir, 'index.html'))\n return new Response(injectConfigScript(html, configScript), {\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n })\n } catch {\n return new Response(\n 'Dashboard not built. Run: npm run build --workspace=apps/api',\n { status: 404, headers: { 'Content-Type': 'text/plain' } },\n )\n }\n }\n }\n }\n }\n return fetchRouter(request)\n }\n\n const honoApp = { fetch: fetchHandler }\n\n const services = {\n profile: profileService,\n consent: consentService,\n visitor: visitorService,\n user: userService,\n }\n\n return {\n router,\n handler,\n fastifyHandler,\n honoApp,\n services,\n storage: storage as StorageAdapter,\n eventBus,\n ready,\n destroy: async () => {\n if (retentionTimer) clearInterval(retentionTimer)\n if (auditRetentionTimer) clearInterval(auditRetentionTimer)\n statsService.dispose()\n if (config.tcf?.enabled) stopGvlRefresh()\n await pluginEngine.destroy()\n },\n }\n}\n\nexport { SQLiteAdapter } from './storage/sqlite/sqlite.adapter'\nexport { MongoDBAdapter } from './storage/mongodb/mongodb.adapter'\nexport { MySQLAdapter } from './storage/mysql/mysql.adapter'\nexport { PostgreSQLAdapter } from './storage/postgresql/postgresql.adapter'\n\nexport { ConsentAction } from './hooks/consent-action'\nexport type { ConsentActionOptions, ConsentActionParams } from './hooks/consent-action'\nexport { CategoryAction } from './hooks/category-action'\nexport type { CategoryActionOptions, CategoryActionParams } from './hooks/category-action'\n\nexport type {\n ConsentiServerConfig,\n BrandingConfig,\n StorageAdapter,\n Profile,\n ConsentDbRecord,\n Visitor,\n AdminUser,\n Role,\n Permission,\n AuditLog,\n ConsentVerifyResult,\n SafeAdminUser,\n} from '@consenti/types'\n","import type {\n ConsentiServerPlugin,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentDbRecord,\n Profile,\n CreateUserInput,\n AdminUser,\n PluginContext,\n} from '@consenti/types'\n\nexport class PluginEngine {\n private plugins: ConsentiServerPlugin[] = []\n\n register(plugins: ConsentiServerPlugin[]): void {\n this.plugins = plugins\n }\n\n async initialize(context: PluginContext): Promise<void> {\n for (const plugin of this.plugins) {\n try { await plugin.initialize?.(context) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" initialize failed:`, err) }\n }\n }\n\n async destroy(): Promise<void> {\n for (const plugin of this.plugins) {\n try { await plugin.destroy?.() }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" destroy failed:`, err) }\n }\n }\n\n async runBeforeConsentSave(data: CreateConsentInput): Promise<CreateConsentInput> {\n let result = data\n for (const plugin of this.plugins) {\n if (!plugin.beforeConsentSave) continue\n try { result = await plugin.beforeConsentSave(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" beforeConsentSave failed:`, err) }\n }\n return result\n }\n\n async runAfterConsentSave(record: ConsentDbRecord): Promise<void> {\n for (const plugin of this.plugins) {\n if (!plugin.afterConsentSave) continue\n try { await plugin.afterConsentSave(record) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterConsentSave failed:`, err) }\n }\n }\n\n async runBeforeConsentUpdate(data: UpdateConsentInput): Promise<UpdateConsentInput> {\n let result = data\n for (const plugin of this.plugins) {\n if (!plugin.beforeConsentUpdate) continue\n try { result = await plugin.beforeConsentUpdate(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" beforeConsentUpdate failed:`, err) }\n }\n return result\n }\n\n async runAfterConsentUpdate(record: ConsentDbRecord): Promise<void> {\n for (const plugin of this.plugins) {\n if (!plugin.afterConsentUpdate) continue\n try { await plugin.afterConsentUpdate(record) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterConsentUpdate failed:`, err) }\n }\n }\n\n async runAfterProfileFetch(profile: Profile): Promise<Profile> {\n let result = profile\n for (const plugin of this.plugins) {\n if (!plugin.afterProfileFetch) continue\n try { result = await plugin.afterProfileFetch(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterProfileFetch failed:`, err) }\n }\n return result\n }\n\n async runBeforeUserCreate(data: CreateUserInput): Promise<CreateUserInput> {\n let result = data\n for (const plugin of this.plugins) {\n if (!plugin.beforeUserCreate) continue\n try { result = await plugin.beforeUserCreate(result) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" beforeUserCreate failed:`, err) }\n }\n return result\n }\n\n async runAfterUserCreate(user: AdminUser): Promise<void> {\n for (const plugin of this.plugins) {\n if (!plugin.afterUserCreate) continue\n try { await plugin.afterUserCreate(user) }\n catch (err) { console.warn(`[Consenti] Plugin \"${plugin.name}\" afterUserCreate failed:`, err) }\n }\n }\n}\n","/**\n * Utilities for materialising server config into the dashboard SPA.\n *\n * Data is injected as an inline <script> that writes window.__CONSENTI_CONFIG__\n * using Object.defineProperty with writable:false + configurable:false, then\n * deep-freezes the value so neither the object nor any nested property can be\n * mutated from the browser.\n */\n\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { extname, join } from 'node:path'\nimport type { AuthConfig, BrandingConfig, ComplianceConfig, ConsentiRuntimeConfig } from '@consenti/types'\n\nfunction resolveappLogoPath(dashboardDir: string, branding: BrandingConfig): string | null {\n const { appLogoPath } = branding\n if (!appLogoPath) return null\n\n if (/^(https?:\\/\\/|data:)/i.test(appLogoPath)) return appLogoPath\n\n if (existsSync(appLogoPath)) {\n const ext = extname(appLogoPath) || '.png'\n const destName = `logo${ext}`\n try {\n if (!existsSync(dashboardDir)) mkdirSync(dashboardDir, { recursive: true })\n copyFileSync(appLogoPath, join(dashboardDir, destName))\n return `./logo${ext}`\n } catch {\n console.warn(`[Consenti] branding: could not copy logo from \"${appLogoPath}\" — logo will not be shown`)\n return null\n }\n }\n\n console.warn(`[Consenti] branding: appLogoPath \"${appLogoPath}\" does not exist — logo will not be shown`)\n return null\n}\n\nexport function buildConsentiRuntimeConfig(\n dashboardDir: string,\n basePath: string,\n branding: BrandingConfig = {},\n compliance: ComplianceConfig = {},\n authMode: AuthConfig['mode'] = 'local',\n): ConsentiRuntimeConfig {\n const resolvedCompliance: ConsentiRuntimeConfig['compliance'] = {}\n if (compliance.type !== undefined) resolvedCompliance.type = compliance.type\n if (compliance.gpc !== undefined) resolvedCompliance.gpc = compliance.gpc\n\n return {\n appName: branding.appName ?? 'Consenti',\n appLogoPath: branding?.appLogoPath ?? \"/logo-dark.svg\", // resolveappLogoPath(dashboardDir, branding),\n hidePoweredBy: branding.hidePoweredBy ?? false,\n basePath,\n compliance: resolvedCompliance,\n authMode,\n }\n}\n\nexport function buildConsentiConfigScript(cfg: ConsentiRuntimeConfig): string {\n // Escape </script> to prevent early tag termination, then emit a self-contained IIFE\n // that locks the config via Object.defineProperty + deep-freeze.\n const json = JSON.stringify(cfg).replace(/<\\/script>/gi, '<\\\\/script>')\n return `<script>(function(){` +\n `function d(o){if(o&&typeof o==='object')Object.keys(o).forEach(function(k){d(o[k])});return Object.freeze(o)}` +\n `Object.defineProperty(window,'__CONSENTI_CONFIG__',{value:d(${json}),writable:false,configurable:false,enumerable:true})` +\n `})()</script>`\n}\n\nexport function injectConfigScript(html: Uint8Array, script: string): string {\n const str = Buffer.from(html).toString('utf-8')\n return str.includes('</head>')\n ? str.replace('</head>', `${script}</head>`)\n : str.replace('</body>', `${script}</body>`)\n}\n","const GVL_URL = 'https://vendor-list.consensu.org/v3/vendor-list.json'\nconst REFRESH_MS = 7 * 24 * 60 * 60 * 1000\n\nexport interface GvlPurpose {\n id: number\n name: string\n description: string\n}\n\nexport interface GvlVendor {\n id: number\n name: string\n purposes?: number[]\n legIntPurposes?: number[]\n flexiblePurposes?: number[]\n specialPurposes?: number[]\n features?: number[]\n specialFeatures?: number[]\n}\n\nexport interface GvlData {\n gvlSpecificationVersion: number\n vendorListVersion: number\n vendors: Record<string, GvlVendor>\n purposes: Record<string, GvlPurpose>\n}\n\nlet cachedGvl: GvlData | null = null\nlet lastFetched = 0\nlet refreshTimer: ReturnType<typeof setInterval> | null = null\n\nfunction isGvlData(d: unknown): d is GvlData {\n return typeof d === 'object' && d !== null &&\n 'vendors' in d && 'purposes' in d &&\n typeof (d as GvlData).vendors === 'object' &&\n typeof (d as GvlData).purposes === 'object'\n}\n\nexport async function getGvl(): Promise<GvlData | null> {\n if (cachedGvl && Date.now() - lastFetched < REFRESH_MS) return cachedGvl\n try {\n const res = await fetch(GVL_URL, { signal: AbortSignal.timeout(15_000) })\n if (!res.ok) return cachedGvl\n const data = await res.json()\n if (!isGvlData(data)) return cachedGvl\n cachedGvl = data\n lastFetched = Date.now()\n return cachedGvl\n } catch {\n return cachedGvl\n }\n}\n\nexport function startGvlRefresh(): void {\n if (refreshTimer) return\n void getGvl()\n refreshTimer = setInterval(() => { void getGvl() }, REFRESH_MS)\n}\n\nexport function stopGvlRefresh(): void {\n if (!refreshTimer) return\n clearInterval(refreshTimer)\n refreshTimer = null\n}\n","const SHARED_SCHEMAS = {\n ConsentRecord: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n visitorId: { type: 'string' },\n profileId: { type: 'string' },\n locale: { type: 'string' },\n consentJson: { type: 'object', additionalProperties: { type: 'string', enum: ['granted', 'denied', 'objected'] } },\n gpcDetected: { type: 'boolean' },\n source: { type: 'string', enum: ['banner', 'api', 'import'] },\n ageVerified: { type: 'boolean' },\n tcfString: { type: 'string' },\n createdAt: { type: 'string', format: 'date-time' },\n updatedAt: { type: 'string', format: 'date-time' },\n },\n },\n CreateConsentInput: {\n type: 'object',\n required: ['visitorId', 'profileId', 'consentJson'],\n properties: {\n visitorId: { type: 'string' },\n profileId: { type: 'string' },\n consentJson: { type: 'object', additionalProperties: { type: 'string' } },\n gpcDetected: { type: 'boolean', default: false },\n source: { type: 'string', enum: ['banner', 'api', 'import'], default: 'banner' },\n ageVerified: { type: 'boolean' },\n parentalConsentToken: { type: 'string' },\n tcfString: { type: 'string' },\n },\n },\n}\n\nexport const OPENAPI_PUBLIC_SPEC = {\n openapi: '3.1.0',\n info: {\n title: 'Consenti Public API',\n version: '0.1.0',\n description: 'Visitor-facing endpoints for reading consent profiles and submitting/verifying consent. Rate-limited; no authentication required.',\n license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0' },\n },\n servers: [\n { url: '/consenti/api', description: 'Consenti Public API' },\n ],\n paths: {\n '/v1/profiles/{id}': {\n get: {\n tags: ['Profile'],\n summary: 'Get profile in its default locale',\n description: 'Returns the resolved profile with banner and modal content for the profile\\'s default locale. Includes `currentLocale`, `locales`, `cookies`, `mainBanner`, `preferenceModal`, and optionally `gpcBanner`.',\n parameters: [\n { name: 'id', in: 'path', required: true, schema: { type: 'string' } },\n ],\n responses: {\n '200': { description: 'Resolved profile for the default locale' },\n '404': { description: 'Profile not found' },\n },\n },\n },\n '/v1/profiles/{id}/{locale}': {\n get: {\n tags: ['Profile'],\n summary: 'Get profile resolved for a specific locale',\n description: 'Same response shape as `GET /v1/profiles/{id}` but resolved for the requested locale, falling back to the default locale for any missing translations.',\n parameters: [\n { name: 'id', in: 'path', required: true, schema: { type: 'string' } },\n { name: 'locale', in: 'path', required: true, schema: { type: 'string', example: 'fr' } },\n ],\n responses: {\n '200': { description: 'Resolved profile for the requested locale' },\n '404': { description: 'Profile not found' },\n },\n },\n },\n '/v1/consent': {\n post: {\n tags: ['Consent'],\n summary: 'Submit consent',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: { $ref: '#/components/schemas/CreateConsentInput' },\n },\n },\n },\n responses: {\n '200': { description: 'Consent created or updated', content: { 'application/json': { schema: { $ref: '#/components/schemas/ConsentRecord' } } } },\n '400': { description: 'Validation error' },\n },\n },\n },\n '/v1/consent/{visitorId}': {\n get: {\n tags: ['Consent'],\n summary: 'Get consent for visitor',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: {\n '200': { description: 'Consent record', content: { 'application/json': { schema: { $ref: '#/components/schemas/ConsentRecord' } } } },\n '404': { description: 'Not found' },\n },\n },\n put: {\n tags: ['Consent'],\n summary: 'Update consent',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Updated consent' } },\n },\n delete: {\n tags: ['Consent'],\n summary: 'GDPR erasure — delete all consent data for visitor',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Erased successfully' } },\n },\n },\n '/v1/consent/{visitorId}/verify': {\n get: {\n tags: ['Consent'],\n summary: 'Verify consent validity',\n parameters: [{ name: 'visitorId', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Verification result' } },\n },\n },\n '/v1/notice-shown': {\n post: {\n tags: ['Consent'],\n summary: 'Record proof that the consent banner was shown',\n description: 'Lightweight, fire-and-forget ping recording that the banner was rendered to a visitor, independent of whether they ever interact with it. Called automatically by `@consenti/ui` on banner mount when `api.enabled` is configured.',\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['visitorId', 'profileId', 'locale'],\n properties: {\n visitorId: { type: 'string' },\n profileId: { type: 'string' },\n locale: { type: 'string' },\n },\n },\n },\n },\n },\n responses: {\n '201': { description: 'Recorded' },\n '400': { description: 'Validation error' },\n },\n },\n },\n },\n components: {\n schemas: SHARED_SCHEMAS,\n },\n}\n\nexport const OPENAPI_ADMIN_SPEC = {\n openapi: '3.1.0',\n info: {\n title: 'Consenti Admin API',\n version: '0.1.0',\n description: 'Authenticated endpoints for managing profiles, consent records, users, analytics, and exports. All routes (except `/auth/login`) require `Authorization: Bearer <JWT>`. Obtain a token via `POST /auth/login`.',\n license: { name: 'Apache 2.0', url: 'https://www.apache.org/licenses/LICENSE-2.0' },\n },\n servers: [\n { url: '/consenti/admin', description: 'Consenti Admin API' },\n ],\n paths: {\n // ── Auth ──────────────────────────────────────────────────────────────\n '/auth/login': {\n post: {\n tags: ['Auth'],\n summary: 'Login with email + password',\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { email: { type: 'string' }, password: { type: 'string' } } } } } },\n responses: { '200': { description: 'JWT token' } },\n },\n },\n '/auth/me': {\n get: {\n tags: ['Auth'],\n summary: 'Get current user',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Current admin user info' } },\n },\n },\n '/auth/logout': {\n post: {\n tags: ['Auth'],\n summary: 'Invalidate session',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Logged out' } },\n },\n },\n '/auth/refresh': {\n post: {\n tags: ['Auth'],\n summary: 'Reissue a fresh token from the current one, extending the session',\n description: 'Used by the dashboard on user activity to implement a sliding inactivity timeout instead of a flat expiry from login.',\n security: [{ BearerAuth: [] }],\n responses: {\n '200': { description: 'New JWT token' },\n '401': { description: 'Current token is missing, invalid, or already expired' },\n },\n },\n },\n '/auth/oidc/authorize': {\n get: {\n tags: ['Auth'],\n summary: 'Start OIDC authorization (PKCE)',\n responses: { '302': { description: 'Redirect to OIDC provider' } },\n },\n },\n '/auth/oidc/callback': {\n get: {\n tags: ['Auth'],\n summary: 'OIDC callback — exchange code for token',\n responses: { '200': { description: 'JWT token' } },\n },\n },\n '/auth/saml/metadata': {\n get: {\n tags: ['Auth'],\n summary: 'SAML SP metadata XML',\n responses: { '200': { description: 'SAML metadata XML', content: { 'application/xml': {} } } },\n },\n },\n '/auth/saml/acs': {\n post: {\n tags: ['Auth'],\n summary: 'SAML Assertion Consumer Service',\n responses: { '200': { description: 'JWT token' } },\n },\n },\n '/auth/totp/setup': {\n post: {\n tags: ['Auth'],\n summary: 'Setup TOTP MFA',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'TOTP secret and QR URL' } },\n },\n },\n '/auth/totp/verify': {\n post: {\n tags: ['Auth'],\n summary: 'Verify and enable TOTP',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'TOTP enabled' } },\n },\n },\n // ── Profiles ──────────────────────────────────────────────────────────\n '/profiles': {\n get: {\n tags: ['Profiles'],\n summary: 'List profiles',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Array of profiles' } },\n },\n post: {\n tags: ['Profiles'],\n summary: 'Create profile',\n security: [{ BearerAuth: [] }],\n responses: { '201': { description: 'Created profile' } },\n },\n },\n // ── Consent Templates ─────────────────────────────────────────────────\n '/consent-templates': {\n get: {\n tags: ['Consent Templates'],\n summary: 'List consent templates',\n description: 'A Consent Template groups parameters (`cookies`, keyed by id) with the categories that own their legal basis (`categories`, keyed by id).',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Array of consent templates' } },\n },\n post: {\n tags: ['Consent Templates'],\n summary: 'Create consent template',\n security: [{ BearerAuth: [] }],\n requestBody: {\n required: true,\n content: {\n 'application/json': {\n schema: {\n type: 'object',\n required: ['name', 'cookies', 'categories'],\n properties: {\n name: { type: 'string' },\n cookies: { type: 'object', description: 'Keyed by parameter id.' },\n categories: { type: 'object', description: 'Keyed by category id. Every parameter id must appear in exactly one category.' },\n },\n },\n },\n },\n },\n responses: { '201': { description: 'Created consent template' }, '400': { description: 'Validation error' } },\n },\n },\n '/consent-templates/{id}': {\n get: {\n tags: ['Consent Templates'],\n summary: 'Get consent template',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Consent template' }, '404': { description: 'Not found' } },\n },\n put: {\n tags: ['Consent Templates'],\n summary: 'Update consent template',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Updated consent template' }, '422': { description: 'Change would break compliance for profiles using this template' } },\n },\n delete: {\n tags: ['Consent Templates'],\n summary: 'Delete consent template',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Deleted' }, '422': { description: 'Template is still used by profiles' } },\n },\n },\n '/consent-templates/{id}/copy': {\n post: {\n tags: ['Consent Templates'],\n summary: 'Duplicate a consent template',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '201': { description: 'Copied consent template' } },\n },\n },\n '/consent-templates/{id}/profile-usage': {\n get: {\n tags: ['Consent Templates'],\n summary: 'List profiles using this consent template',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Array of profile summaries' } },\n },\n },\n // ── Consents ──────────────────────────────────────────────────────────\n '/consents': {\n get: {\n tags: ['Consents'],\n summary: 'List consent records',\n security: [{ BearerAuth: [] }],\n parameters: [\n { name: 'page', in: 'query', schema: { type: 'integer' } },\n { name: 'profileId', in: 'query', schema: { type: 'string' } },\n { name: 'from', in: 'query', schema: { type: 'string', format: 'date' } },\n { name: 'to', in: 'query', schema: { type: 'string', format: 'date' } },\n { name: 'q', in: 'query', schema: { type: 'string' }, description: 'Free-text search across visitorId, profileId, locale, source' },\n ],\n responses: { '200': { description: 'Paginated consent records' } },\n },\n },\n // ── Stats ─────────────────────────────────────────────────────────────\n '/stats/overview': {\n get: {\n tags: ['Stats'],\n summary: 'Overview statistics',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Totals and acceptance rates' } },\n },\n },\n '/stats/timeline': {\n get: {\n tags: ['Stats'],\n summary: 'Daily consent timeline',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'days', in: 'query', schema: { type: 'integer', default: 30 } }],\n responses: { '200': { description: 'Array of date+count' } },\n },\n },\n '/stats/countries': {\n get: {\n tags: ['Stats'],\n summary: 'Consent by country',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Country counts' } },\n },\n },\n '/stats/gpc': {\n get: {\n tags: ['Stats'],\n summary: 'GPC detection stats',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'GPC rate and counts' } },\n },\n },\n // ── Export ────────────────────────────────────────────────────────────\n '/export/consents': {\n get: {\n tags: ['Export'],\n summary: 'Export consent records',\n security: [{ BearerAuth: [] }],\n parameters: [\n { name: 'format', in: 'query', schema: { type: 'string', enum: ['csv', 'json'] } },\n { name: 'from', in: 'query', schema: { type: 'string', format: 'date' } },\n { name: 'to', in: 'query', schema: { type: 'string', format: 'date' } },\n ],\n responses: { '200': { description: 'CSV or JSON download' } },\n },\n },\n // ── Tenants ───────────────────────────────────────────────────────────\n '/tenants': {\n get: {\n tags: ['Sites'],\n summary: 'List all sites/tenants',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Array of tenants' } },\n },\n post: {\n tags: ['Sites'],\n summary: 'Create a site/tenant',\n security: [{ BearerAuth: [] }],\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { name: { type: 'string' }, slug: { type: 'string' } }, required: ['name', 'slug'] } } } },\n responses: { '201': { description: 'Created tenant' } },\n },\n },\n '/tenants/{id}': {\n put: {\n tags: ['Sites'],\n summary: 'Update a site/tenant',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Updated tenant' } },\n },\n delete: {\n tags: ['Sites'],\n summary: 'Delete a site/tenant',\n security: [{ BearerAuth: [] }],\n parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],\n responses: { '200': { description: 'Deleted' } },\n },\n },\n // ── Setup Wizard ──────────────────────────────────────────────────────\n // One-time first-run flow, gated by tenant_settings.setup_completed — see\n // plans/PENDING-api-setup-wizard.md. Never reset from the dashboard once complete.\n '/setup/status': {\n get: {\n tags: ['Setup'],\n summary: 'Whether the first-run setup wizard has been completed for this tenant',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: '{ completed: boolean }' } },\n },\n },\n '/setup/config': {\n get: {\n tags: ['Setup'],\n summary: 'Resolved server config (secrets redacted) plus production-readiness flags',\n description: 'The same merged `DEFAULT_CONFIG` + user config `createConsenti` computes at boot. `auth.adminPassword`, `auth.jwtSecret`, `consentSigningKey`, storage credentials, and OIDC/SAML secrets are replaced with a redaction marker.',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: '{ config: object, usingJsonStorage: boolean, usingDefaultCredentials: boolean }' } },\n },\n },\n '/setup/compliance-groups': {\n get: {\n tags: ['Setup'],\n summary: 'The 8 built-in compliance groups with label/description/regulation metadata',\n security: [{ BearerAuth: [] }],\n responses: { '200': { description: 'Array of compliance group descriptors' } },\n },\n },\n '/setup/seed-profiles': {\n post: {\n tags: ['Setup'],\n summary: 'Seed default profiles for the selected compliance groups',\n description: 'Idempotent — skips any group that already has an active profile. `groups` must be a subset of the 8 built-in compliance group ids.',\n security: [{ BearerAuth: [] }],\n requestBody: { required: true, content: { 'application/json': { schema: { type: 'object', properties: { groups: { type: 'array', items: { type: 'string' } } }, required: ['groups'] } } } },\n responses: {\n '200': { description: '{ seeded: string[] }' },\n '400': { description: 'groups is missing, not an array of strings, or contains an unknown compliance group id' },\n '409': { description: 'Setup has already been completed for this tenant' },\n },\n },\n },\n '/setup/complete': {\n post: {\n tags: ['Setup'],\n summary: 'Mark the first-run setup wizard as complete for this tenant',\n description: 'Called on both wizard finish and skip. Never reset from the dashboard once true.',\n security: [{ BearerAuth: [] }],\n responses: {\n '200': { description: 'Updated tenant settings' },\n '409': { description: 'Setup has already been completed for this tenant' },\n },\n },\n },\n },\n components: {\n securitySchemes: {\n BearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },\n },\n schemas: SHARED_SCHEMAS,\n },\n}\n","import { createHash, createHmac, randomUUID, scryptSync, randomBytes, timingSafeEqual } from 'node:crypto'\n\nexport { randomUUID }\n\n// Prefixed resource IDs — the prefix identifies the resource type at a glance (in audit log\n// resourceId values, error messages, support tickets), the same idea as Stripe/GitHub-style IDs.\n//\n// 16 base62 characters is ~95 bits of randomness — collisions only become a realistic risk\n// (birthday bound) around 2^47 ≈ 140 trillion IDs of the *same type*, far beyond anything this\n// project will ever generate, while staying shorter than a bare UUID (36 chars) once the 5-char\n// prefix is added (21 chars total) — short enough to read out or paste into a support ticket.\nconst BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst ID_SUFFIX_LEN = 16\n\nfunction randomBase62(length: number): string {\n const bytes = randomBytes(length)\n let out = ''\n for (let i = 0; i < length; i++) out += BASE62[bytes[i]! % BASE62.length]\n return out\n}\n\nfunction prefixedId(prefix: string): string {\n return `${prefix}_${randomBase62(ID_SUFFIX_LEN)}`\n}\n\nexport function randomProfileId(): string { return prefixedId('prof') }\nexport function randomVisitorId(): string { return prefixedId('visi') }\nexport function randomConsentId(): string { return prefixedId('cons') }\nexport function randomConsentTemplateId(): string { return prefixedId('ctem') }\nexport function randomUITemplateId(): string { return prefixedId('utem') }\n\nexport function hashIp(ip: string): string {\n return createHash('sha256').update(ip).digest('hex')\n}\n\nexport function hashUserAgent(ua: string): string {\n return createHash('sha256').update(ua).digest('hex')\n}\n\nexport function signHmac(payload: string, secret: string): string {\n return createHmac('sha256', secret).update(payload).digest('hex')\n}\n\nexport function verifyHmac(payload: string, signature: string, secret: string): boolean {\n const expected = Buffer.from(signHmac(payload, secret), 'hex')\n const actual = Buffer.from(signature, 'hex')\n if (expected.length !== actual.length) return false\n return timingSafeEqual(expected, actual)\n}\n\nexport function hashPassword(password: string): string {\n const salt = randomBytes(16).toString('hex')\n const hash = scryptSync(password, salt, 64).toString('hex')\n return `${salt}:${hash}`\n}\n\nexport function verifyPassword(password: string, stored: string): boolean {\n const [salt, hash] = stored.split(':')\n if (!salt || !hash) return false\n const derived = scryptSync(password, salt, 64).toString('hex')\n return timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(derived, 'hex'))\n}\n\nexport function signJwt(payload: Record<string, unknown>, secret: string, expiresInSeconds = 3600): string {\n const now = Math.floor(Date.now() / 1000)\n const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).replace(/=/g, '')\n const body = btoa(JSON.stringify({ ...payload, iat: now, exp: now + expiresInSeconds })).replace(/=/g, '')\n const sig = signHmac(`${header}.${body}`, secret)\n .match(/.{2}/g)!\n .map(h => String.fromCharCode(parseInt(h, 16)))\n .join('')\n const sigEncoded = btoa(sig).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n return `${header}.${body}.${sigEncoded}`\n}\n\nexport function verifyJwt(token: string, secret: string): Record<string, unknown> | null {\n const parts = token.split('.')\n if (parts.length !== 3) return null\n const [header, body, sigEncoded] = parts\n if (!header || !body || !sigEncoded) return null\n const expected = signHmac(`${header}.${body}`, secret)\n const sigBytes = atob(sigEncoded.replace(/-/g, '+').replace(/_/g, '/'))\n const actualHex = Array.from(sigBytes).map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join('')\n const expectedBuf = Buffer.from(expected, 'hex')\n const actualBuf = Buffer.from(actualHex, 'hex')\n if (expectedBuf.length !== actualBuf.length || !timingSafeEqual(expectedBuf, actualBuf)) return null\n try {\n const payload = JSON.parse(atob(body)) as Record<string, unknown>\n const exp = payload['exp']\n if (typeof exp === 'number' && Date.now() / 1000 > exp) return null\n return payload\n } catch {\n return null\n }\n}\n","import { existsSync, accessSync, constants, mkdirSync } from 'node:fs'\nimport { resolve, extname, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nexport interface StoragePaths {\n /** Full path to the adapter's DB file (inside basePath/db/). */\n dbPath: string\n /** Full path to the profiles directory (basePath/profiles/). */\n profilesDir: string\n /** Full path to the logs directory (basePath/logs/). */\n logsDir: string\n /** Resolved base directory path. */\n basePath: string\n}\n\nconst DB_FILE_NAMES: Record<string, string> = {\n 'node:sqlite': 'consenti.db',\n 'better-sqlite3': 'consenti.db',\n 'node-sqlite3-wasm': 'consenti.db',\n sqlite: 'consenti.db',\n json: 'consenti-data.json',\n mongodb: '',\n mysql: '',\n postgresql: '',\n}\n\nconst _thisDir = dirname(fileURLToPath(import.meta.url))\n\n/** Guard: path must not be inside the process's own module directory. */\nfunction isInsidePackageInstall(resolved: string): boolean {\n const nodeModulesSegment = resolved.includes('node_modules')\n const isInDist = resolved.startsWith(resolve(_thisDir, '..', '..'))\n return nodeModulesSegment || isInDist\n}\n\n/**\n * Resolves and validates the storage base directory from `config.storage.path`.\n *\n * Backward compat: if the path has a file extension (.json, .db) it is treated as a\n * legacy file path — the parent directory is used instead and a deprecation warning is logged.\n */\nexport function resolveStoragePaths(rawPath: string, driver: string): StoragePaths {\n let basePath = resolve(rawPath)\n\n const ext = extname(basePath)\n if (ext) {\n console.warn(\n `[consenti] storage.path \"${rawPath}\" looks like a file path (has extension \"${ext}\"). ` +\n `Treating parent directory as storage root. Update your config to pass a directory path.`,\n )\n basePath = dirname(basePath)\n }\n\n if (isInsidePackageInstall(basePath)) {\n throw new Error(\n `[consenti] storage.path \"${basePath}\" is inside the package installation directory. ` +\n `Choose a writable directory outside node_modules.`,\n )\n }\n\n if (!existsSync(basePath)) {\n mkdirSync(basePath, { recursive: true })\n }\n\n try {\n accessSync(basePath, constants.W_OK)\n } catch {\n throw new Error(`[consenti] storage.path \"${basePath}\" is not writable.`)\n }\n\n const dbDir = join(basePath, 'db')\n const profilesDir = join(basePath, 'profiles')\n const logsDir = join(basePath, 'logs')\n\n mkdirSync(dbDir, { recursive: true })\n mkdirSync(profilesDir, { recursive: true })\n mkdirSync(logsDir, { recursive: true })\n\n const dbFileName = DB_FILE_NAMES[driver] ?? 'consenti.db'\n const dbPath = dbFileName ? join(dbDir, dbFileName) : ''\n\n return { basePath, dbPath, profilesDir, logsDir }\n}\n","// Single source of truth for schema definitions, seed data, and SQL generation.\n// Document DBs (JSON, MongoDB) use the JS objects directly.\n// SQL DBs (SQLite, PostgreSQL, MySQL) import the generated SQL exports.\n\n// ── Schema definition types ───────────────────────────────────────────────────\n\n/** Abstract column types — each SQL dialect maps these to its native type. */\nexport type ColType =\n | 'pk' // String primary key\n | 'text' // Short-medium text (TEXT in SQLite/PG; VARCHAR(255) in MySQL)\n | 'text_sm' // Short code ≤20 (TEXT in SQLite/PG; VARCHAR(20) in MySQL)\n | 'text_md' // Medium text ≤100 (TEXT in SQLite/PG; VARCHAR(100) in MySQL)\n | 'text_geo' // Geo code ≤10 (TEXT in SQLite/PG; VARCHAR(10) in MySQL)\n | 'text_hash' // Hash string ≤64 (TEXT in SQLite/PG; VARCHAR(64) in MySQL)\n | 'longtext' // Long text (TEXT in SQLite/PG; LONGTEXT in MySQL)\n | 'json' // JSON blob (TEXT in SQLite; JSONB in PG; LONGTEXT in MySQL)\n | 'json_ordered' // JSON blob whose object-key order is meaningful (authored button/category/\n // cookie maps) — same as 'json' except in PG, where it uses the plain JSON\n // type instead of JSONB. JSONB's binary storage format re-sorts object keys\n // (by key length, then lexicographically) on every write, silently discarding\n // authored order; plain JSON stores the exact input text. (TEXT in SQLite; JSON in PG; LONGTEXT in MySQL)\n | 'int' // Integer\n | 'bool' // Boolean (INTEGER 0/1 in SQLite; BOOLEAN in PG; TINYINT(1) in MySQL)\n | 'ts' // Timestamp with current-time default\n\nexport interface ColDef {\n n: string // column name\n t: ColType\n null?: true // nullable; omit = NOT NULL\n def?: string | number | boolean // '$now'→now(), '$arr'→'[]', '$obj'→'{}'\n unique?: true\n ref?: string // REFERENCES <ref>(id)\n onDel?: 'CASCADE'\n mysqlUpd?: true // MySQL: ON UPDATE CURRENT_TIMESTAMP\n}\n\nexport interface TableDef {\n name: string\n cols: ColDef[]\n pk?: string[] // composite primary key\n uq?: { cols: string[] }[] // composite unique constraints\n idx?: { cols: string[] }[] // secondary indexes — generated for SQLite/Postgres/MySQL,\n // and consumed as-is by the Mongo adapter's createIndexes().\n // Single source of truth: add a query pattern here once,\n // it reaches every storage driver.\n}\n\n// ── Table definitions ─────────────────────────────────────────────────────────\n\nconst tenants: TableDef = {\n name: 'tenants',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'name', t: 'text' },\n { n: 'slug', t: 'text', unique: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\nconst profiles: TableDef = {\n name: 'profiles',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text' },\n { n: 'default_locale', t: 'text_sm', def: 'en' },\n // Incremented in place on every save — the row is mutated, not replaced.\n { n: 'version', t: 'int', def: 1 },\n { n: 'profile_json', t: 'json_ordered' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n idx: [{ cols: ['tenant_id'] }],\n // SQLite adapters additionally hand-roll a `(tenant_id, json_extract(profile_json,'$.complianceGroup'),\n // json_extract(profile_json,'$.isActive'))` expression index for findActiveProfileByComplianceGroup —\n // expression indexes aren't representable in this plain-column declarative system, and profiles is a\n // small, per-tenant table (not a millions-of-rows target), so it stays hand-written there.\n}\n\nconst consentRecords: TableDef = {\n name: 'consent_records',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'visitor_id', t: 'text' },\n { n: 'profile_id', t: 'text', ref: 'profiles' },\n { n: 'locale', t: 'text_sm' },\n { n: 'consent_json', t: 'json' },\n { n: 'gpc_detected', t: 'bool', def: false },\n { n: 'source', t: 'text_sm' },\n { n: 'age_verified', t: 'bool', def: false },\n { n: 'parental_consent_token', t: 'longtext', null: true },\n { n: 'tcf_string', t: 'longtext', null: true },\n { n: 'signature', t: 'longtext', null: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n uq: [{ cols: ['visitor_id', 'profile_id'] }],\n // `uq` above already gives every dialect a (visitor_id, profile_id) index, which — by the\n // leftmost-prefix rule every SQL engine follows — also serves lookups/prefix-searches on\n // visitor_id alone, so it isn't repeated here.\n idx: [\n { cols: ['tenant_id', 'created_at'] }, // base list/sort (no profileId filter)\n { cols: ['tenant_id', 'profile_id', 'created_at'] }, // list/sort filtered by profileId\n { cols: ['locale'] }, // q-search column\n { cols: ['source'] }, // q-search column\n ],\n}\n\nconst consentHistory: TableDef = {\n name: 'consent_history',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'consent_record_id', t: 'text', ref: 'consent_records' },\n { n: 'visitor_id', t: 'text' },\n { n: 'old_json', t: 'json', null: true },\n { n: 'new_json', t: 'json' },\n { n: 'action', t: 'text_sm' },\n { n: 'created_at', t: 'ts', def: '$now' },\n ],\n idx: [{ cols: ['visitor_id'] }],\n}\n\nconst visitors: TableDef = {\n name: 'visitors',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'visitor_id', t: 'text', unique: true },\n { n: 'country', t: 'text_geo', null: true },\n { n: 'region', t: 'text_md', null: true },\n { n: 'city', t: 'text_md', null: true },\n { n: 'ip_hash', t: 'text_hash', null: true },\n { n: 'user_agent_hash', t: 'text_hash', null: true },\n { n: 'first_seen', t: 'ts', def: '$now' },\n { n: 'last_seen', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n // visitor_id's `unique: true` above already gives every dialect an index covering visitor_id.\n idx: [\n { cols: ['tenant_id', 'first_seen'] },\n { cols: ['country'] }, // q-search column\n ],\n}\n\nconst noticeShown: TableDef = {\n name: 'notice_shown',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'visitor_id', t: 'text' },\n { n: 'profile_id', t: 'text', ref: 'profiles' },\n { n: 'locale', t: 'text_sm' },\n { n: 'created_at', t: 'ts', def: '$now' },\n ],\n idx: [{ cols: ['visitor_id'] }],\n}\n\nconst users: TableDef = {\n name: 'users',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text' },\n { n: 'email', t: 'text', unique: true },\n { n: 'password_hash', t: 'text' },\n { n: 'is_active', t: 'bool', def: true },\n { n: 'totp_secret', t: 'longtext', null: true },\n { n: 'totp_enabled', t: 'bool', def: false },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\n/** SQLite-only variant — adds `allowed_tenants`, which only the SQLite adapters currently\n * read/write (see `RawUser.allowed_tenants` in each `sqlite/*.adapter.ts`). PostgreSQL/MySQL\n * don't support per-user tenant scoping yet. */\nconst usersSqlite: TableDef = {\n ...users,\n cols: [...users.cols, { n: 'allowed_tenants', t: 'json', null: true }],\n}\n\nconst roles: TableDef = {\n name: 'roles',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'name', t: 'text_md' },\n { n: 'description', t: 'text', null: true },\n ],\n}\n\nconst permissions: TableDef = {\n name: 'permissions',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'name', t: 'text_md', unique: true },\n { n: 'description', t: 'text', null: true },\n ],\n}\n\nconst userRoles: TableDef = {\n name: 'user_roles',\n cols: [\n { n: 'user_id', t: 'text', ref: 'users' },\n { n: 'role_id', t: 'text', ref: 'roles' },\n { n: 'tenant_id', t: 'text', def: 'default' },\n ],\n pk: ['user_id', 'role_id', 'tenant_id'],\n idx: [{ cols: ['user_id'] }],\n}\n\nconst rolePermissions: TableDef = {\n name: 'role_permissions',\n cols: [\n { n: 'role_id', t: 'text', ref: 'roles' },\n { n: 'permission_id', t: 'text', ref: 'permissions' },\n ],\n pk: ['role_id', 'permission_id'],\n idx: [{ cols: ['role_id'] }],\n}\n\nconst auditLogs: TableDef = {\n name: 'audit_logs',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants' },\n { n: 'user_id', t: 'text', null: true },\n { n: 'action', t: 'text_md' },\n { n: 'resource_type', t: 'text_md' },\n { n: 'resource_id', t: 'text', null: true },\n { n: 'old_data', t: 'json', null: true },\n { n: 'new_data', t: 'json', null: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n ],\n idx: [\n { cols: ['tenant_id', 'created_at'] },\n { cols: ['action'] },\n { cols: ['resource_type'] },\n { cols: ['resource_id'] },\n { cols: ['user_id'] },\n ],\n}\n\nconst apiKeys: TableDef = {\n name: 'api_keys',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'key_hash', t: 'text_hash', unique: true },\n { n: 'name', t: 'text' },\n { n: 'is_active', t: 'bool', def: true },\n { n: 'created_by', t: 'text', null: true },\n { n: 'expire_by', t: 'ts', null: true },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\nconst tenantSettings: TableDef = {\n name: 'tenant_settings',\n cols: [\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'allowed_origins_json', t: 'json', def: '$arr' },\n { n: 'admin_allowed_origins_json', t: 'json', def: '$arr' },\n // First-run setup wizard completion flag — set once, never reset (see setup.routes.ts).\n { n: 'setup_completed', t: 'bool', def: false },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n pk: ['tenant_id'],\n}\n\nconst consentTemplates: TableDef = {\n name: 'consent_templates',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'name', t: 'text' },\n { n: 'cookies_json', t: 'json_ordered', def: '$obj' },\n { n: 'categories_json', t: 'json_ordered', def: '$obj' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\nconst uiTemplates: TableDef = {\n name: 'ui_templates',\n cols: [\n { n: 'id', t: 'pk' },\n { n: 'tenant_id', t: 'text', ref: 'tenants', onDel: 'CASCADE' },\n { n: 'name', t: 'text' },\n { n: 'settings_json', t: 'json_ordered', def: '$obj' },\n { n: 'created_at', t: 'ts', def: '$now' },\n { n: 'updated_at', t: 'ts', def: '$now', mysqlUpd: true },\n ],\n}\n\n// ── Exported table definition groups ─────────────────────────────────────────\n// No installations predate this schema, so every adapter creates the full current table set\n// in one pass on first connect — no incremental/versioned migration steps. `migrate()` in each\n// SQL adapter keeps a `schema_version`/`PRAGMA user_version` check so *future* real migrations\n// have somewhere to hook in; there's just nothing to gate behind a version yet.\n\n/** All tables, SQLite dialect — `usersSqlite` includes `allowed_tenants`. */\nexport const ALL_TABLES_SQLITE: readonly TableDef[] = [\n tenants, profiles, consentRecords, consentHistory, visitors,\n usersSqlite, roles, permissions, userRoles, rolePermissions, auditLogs,\n apiKeys, consentTemplates, uiTemplates, tenantSettings, noticeShown,\n]\n\n/** All tables, PostgreSQL/MySQL dialect. */\nexport const ALL_TABLES_FULL: readonly TableDef[] = [\n tenants, profiles, consentRecords, consentHistory, visitors,\n users, roles, permissions, userRoles, rolePermissions, auditLogs,\n apiKeys, consentTemplates, uiTemplates, tenantSettings, noticeShown,\n]\n\n// ── SQL generators ────────────────────────────────────────────────────────────\n\nfunction sqliteDef(def: string | number | boolean): string {\n if (def === '$now') return `DEFAULT (datetime('now'))`\n if (def === '$arr') return `DEFAULT '[]'`\n if (def === '$obj') return `DEFAULT '{}'`\n if (def === true) return 'DEFAULT 1'\n if (def === false) return 'DEFAULT 0'\n if (typeof def === 'string') return `DEFAULT '${def}'`\n return `DEFAULT ${def}`\n}\n\nfunction buildColSQLite(c: ColDef): string {\n if (c.t === 'pk') return `${c.n} TEXT PRIMARY KEY`\n const type = (c.t === 'int' || c.t === 'bool') ? 'INTEGER' : 'TEXT'\n const parts = [`${c.n} ${type}`]\n if (!c.null) parts.push('NOT NULL')\n if (c.def !== undefined) parts.push(sqliteDef(c.def))\n if (c.unique) parts.push('UNIQUE')\n if (c.ref) { parts.push(`REFERENCES ${c.ref}(id)`); if (c.onDel) parts.push(`ON DELETE ${c.onDel}`) }\n return parts.join(' ')\n}\n\nfunction buildTableSQLite(t: TableDef): string {\n const lines = t.cols.map(c => ` ${buildColSQLite(c)}`)\n if (t.pk) lines.push(` PRIMARY KEY (${t.pk.join(', ')})`)\n if (t.uq) for (const u of t.uq) lines.push(` UNIQUE(${u.cols.join(', ')})`)\n return `CREATE TABLE IF NOT EXISTS ${t.name} (\\n${lines.join(',\\n')}\\n);`\n}\n\nfunction idxName(t: TableDef, cols: string[]): string {\n return `idx_${t.name}_${cols.join('_')}`\n}\n\nfunction buildIndexesSQLite(t: TableDef): string {\n return (t.idx ?? [])\n .map(i => `CREATE INDEX IF NOT EXISTS ${idxName(t, i.cols)} ON ${t.name} (${i.cols.join(', ')});`)\n .join('\\n')\n}\n\nfunction pgDef(def: string | number | boolean): string {\n if (def === '$now') return 'DEFAULT NOW()'\n if (def === '$arr') return `DEFAULT '[]'`\n if (def === '$obj') return `DEFAULT '{}'`\n if (def === true) return 'DEFAULT TRUE'\n if (def === false) return 'DEFAULT FALSE'\n if (typeof def === 'string') return `DEFAULT '${def}'`\n return `DEFAULT ${def}`\n}\n\nfunction buildColPg(c: ColDef): string {\n if (c.t === 'pk') return `${c.n} TEXT PRIMARY KEY`\n const type = c.t === 'json' ? 'JSONB' : c.t === 'json_ordered' ? 'JSON' : c.t === 'int' ? 'INTEGER' : c.t === 'bool' ? 'BOOLEAN' : c.t === 'ts' ? 'TIMESTAMPTZ' : 'TEXT'\n const parts = [`${c.n} ${type}`]\n if (!c.null) parts.push('NOT NULL')\n if (c.def !== undefined) parts.push(pgDef(c.def))\n if (c.unique) parts.push('UNIQUE')\n if (c.ref) { parts.push(`REFERENCES ${c.ref}(id)`); if (c.onDel) parts.push(`ON DELETE ${c.onDel}`) }\n return parts.join(' ')\n}\n\nfunction buildTablePg(t: TableDef): string {\n const lines = t.cols.map(c => ` ${buildColPg(c)}`)\n if (t.pk) lines.push(` PRIMARY KEY (${t.pk.join(', ')})`)\n if (t.uq) for (const u of t.uq) lines.push(` UNIQUE (${u.cols.join(', ')})`)\n return `CREATE TABLE IF NOT EXISTS ${t.name} (\\n${lines.join(',\\n')}\\n);`\n}\n\nfunction buildIndexesPg(t: TableDef): string {\n // Only runs once, inside the same version-gated fresh-install transaction as the CREATE TABLE\n // statements above (see PostgreSQLAdapter#migrate), so IF NOT EXISTS is defensive, not required.\n return (t.idx ?? [])\n .map(i => `CREATE INDEX IF NOT EXISTS ${idxName(t, i.cols)} ON ${t.name} (${i.cols.join(', ')});`)\n .join('\\n')\n}\n\nconst MYSQL_TEXT: Partial<Record<ColType, string>> = {\n text: 'VARCHAR(255)', text_sm: 'VARCHAR(20)', text_md: 'VARCHAR(100)',\n text_geo: 'VARCHAR(10)', text_hash: 'VARCHAR(64)', longtext: 'LONGTEXT', json: 'LONGTEXT', json_ordered: 'LONGTEXT',\n}\n\nfunction mysqlDef(def: string | number | boolean): string {\n if (def === '$now') return 'DEFAULT CURRENT_TIMESTAMP'\n if (def === '$arr') return `DEFAULT '[]'`\n if (def === '$obj') return `DEFAULT '{}'`\n if (def === true) return 'DEFAULT 1'\n if (def === false) return 'DEFAULT 0'\n if (typeof def === 'string') return `DEFAULT '${def}'`\n return `DEFAULT ${def}`\n}\n\nfunction buildColMySQL(c: ColDef): string {\n // Prefixed IDs (e.g. `cons_<40 hex chars>`, see utils/crypto.ts) run to 45 chars — sized with\n // headroom above a bare UUID's 36.\n if (c.t === 'pk') return `${c.n} VARCHAR(64) PRIMARY KEY`\n const type = c.t === 'int' ? 'INT' : c.t === 'bool' ? 'TINYINT(1)' : c.t === 'ts' ? 'DATETIME' : (MYSQL_TEXT[c.t] ?? 'TEXT')\n const parts = [`${c.n} ${type}`]\n if (!c.null) parts.push('NOT NULL')\n if (c.def !== undefined) parts.push(mysqlDef(c.def))\n if (c.mysqlUpd) parts.push('ON UPDATE CURRENT_TIMESTAMP')\n if (c.unique) parts.push('UNIQUE')\n if (c.ref) { parts.push(`REFERENCES ${c.ref}(id)`); if (c.onDel) parts.push(`ON DELETE ${c.onDel}`) }\n return parts.join(' ')\n}\n\nfunction buildTableMySQL(t: TableDef): string {\n const lines = t.cols.map(c => ` ${buildColMySQL(c)}`)\n if (t.pk) lines.push(` PRIMARY KEY (${t.pk.join(', ')})`)\n if (t.uq) for (const u of t.uq) lines.push(` UNIQUE KEY uq_${u.cols.join('_')} (${u.cols.join(', ')})`)\n return `CREATE TABLE IF NOT EXISTS ${t.name} (\\n${lines.join(',\\n')}\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`\n}\n\nfunction buildIndexesMySQL(t: TableDef): string {\n // MySQL's CREATE INDEX has no IF NOT EXISTS — safe regardless, since this only runs once,\n // inside the same version-gated fresh-install pass as the CREATE TABLE statements above.\n return (t.idx ?? [])\n .map(i => `CREATE INDEX ${idxName(t, i.cols)} ON ${t.name} (${i.cols.join(', ')});`)\n .join('\\n')\n}\n\nfunction toSQLite(tables: readonly TableDef[]): string {\n const idx = tables.map(buildIndexesSQLite).filter(Boolean).join('\\n')\n return idx ? `${tables.map(buildTableSQLite).join('\\n')}\\n${idx}` : tables.map(buildTableSQLite).join('\\n')\n}\nfunction toPostgres(tables: readonly TableDef[]): string {\n const idx = tables.map(buildIndexesPg).filter(Boolean).join('\\n')\n return idx ? `${tables.map(buildTablePg).join('\\n')}\\n${idx}` : tables.map(buildTablePg).join('\\n')\n}\nfunction toMySQL(tables: readonly TableDef[]): string {\n const idx = tables.map(buildIndexesMySQL).filter(Boolean).join('\\n')\n return idx ? `${tables.map(buildTableMySQL).join('\\n')}\\n${idx}` : tables.map(buildTableMySQL).join('\\n')\n}\n\n// ── Index list for document stores ────────────────────────────────────────────\n// MongoDB is schemaless, so it can't consume CREATE INDEX SQL — it consumes this list directly.\n// Same source of truth as the SQL dialects above: add a query pattern once (via `idx`/`uq`/\n// `unique` on a TableDef), every driver picks it up.\n\nexport interface IndexDef { table: string; cols: string[]; unique?: true }\n\nfunction collectIndexes(tables: readonly TableDef[]): IndexDef[] {\n const result: IndexDef[] = []\n for (const t of tables) {\n for (const u of t.uq ?? []) result.push({ table: t.name, cols: u.cols, unique: true })\n for (const c of t.cols) if (c.unique) result.push({ table: t.name, cols: [c.n], unique: true })\n for (const i of t.idx ?? []) result.push({ table: t.name, cols: i.cols })\n }\n return result\n}\n\n/** All indexes (unique + secondary), PostgreSQL/MySQL/Mongo dialect table names. */\nexport const ALL_INDEXES: readonly IndexDef[] = collectIndexes(ALL_TABLES_FULL)\n\n// ── Schema SQL exports ────────────────────────────────────────────────────────\n// One block per dialect containing the full current schema — applied once on first connect.\n\n/** SQLite full schema — all tables with current column set. */\nexport const SCHEMA_SQL_SQLITE = toSQLite(ALL_TABLES_SQLITE)\n\n/** PostgreSQL full schema — all tables with current column set. */\nexport const SCHEMA_SQL_POSTGRES =\n toPostgres(ALL_TABLES_FULL) +\n '\\nCREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY);\\n'\n\n/** MySQL full schema — all tables with current column set. */\nexport const SCHEMA_SQL_MYSQL =\n toMySQL(ALL_TABLES_FULL) +\n '\\nCREATE TABLE IF NOT EXISTS schema_version (\\n version INT NOT NULL PRIMARY KEY\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\\n'\n\n// ── Core data ─────────────────────────────────────────────────────────────────\n\nexport interface SeedPermission { id: string; name: string; description: string }\nexport interface SeedRole { id: string; tenantId: string; name: string; description: string }\nexport interface SeedRolePermission { roleId: string; permissionId: string }\n\nexport const SEED_TENANT = { id: 'default', name: 'Default', slug: 'default' } as const\n\nexport const SEED_PERMISSIONS: SeedPermission[] = [\n { id: 'perm_profile_create', name: 'profile:create', description: 'Create profiles' },\n { id: 'perm_profile_update', name: 'profile:update', description: 'Update profiles' },\n { id: 'perm_profile_delete', name: 'profile:delete', description: 'Delete profiles' },\n { id: 'perm_profile_view', name: 'profile:view', description: 'View profiles' },\n { id: 'perm_consent_view', name: 'consent:view', description: 'View consent records' },\n { id: 'perm_visitor_view', name: 'visitor:view', description: 'View visitor records' },\n { id: 'perm_user_create', name: 'user:create', description: 'Create users' },\n { id: 'perm_user_update', name: 'user:update', description: 'Update users' },\n { id: 'perm_user_delete', name: 'user:delete', description: 'Delete users' },\n { id: 'perm_user_view', name: 'user:view', description: 'View users' },\n { id: 'perm_role_create', name: 'role:create', description: 'Create roles' },\n { id: 'perm_role_update', name: 'role:update', description: 'Update roles' },\n { id: 'perm_role_delete', name: 'role:delete', description: 'Delete roles' },\n { id: 'perm_role_view', name: 'role:view', description: 'View roles' },\n { id: 'perm_audit_view', name: 'audit:view', description: 'View audit logs' },\n { id: 'perm_settings_update', name: 'settings:update', description: 'Update settings' },\n { id: 'perm_export_run', name: 'export:run', description: 'Run data exports' },\n { id: 'perm_stats_view', name: 'stats:view', description: 'View reporting and analytics' },\n]\n\nexport const SEED_ROLES: SeedRole[] = [\n { id: 'role_super_admin', tenantId: 'default', name: 'super_admin', description: 'Full system access' },\n { id: 'role_admin', tenantId: 'default', name: 'admin', description: 'Administrative access' },\n { id: 'role_compliance_officer', tenantId: 'default', name: 'compliance_officer', description: 'Compliance read access' },\n { id: 'role_viewer', tenantId: 'default', name: 'viewer', description: 'Read-only access' },\n]\n\nconst adminExcluded = new Set(['role:delete', 'user:delete'])\nconst complianceAllowed = new Set(['consent:view', 'visitor:view', 'audit:view', 'profile:view', 'export:run', 'stats:view'])\nconst viewerAllowed = new Set(['consent:view', 'visitor:view', 'profile:view', 'stats:view'])\n\nexport const SEED_ROLE_PERMISSIONS: SeedRolePermission[] = [\n ...SEED_PERMISSIONS.map(p => ({ roleId: 'role_super_admin', permissionId: p.id })),\n ...SEED_PERMISSIONS.filter(p => !adminExcluded.has(p.name)).map(p => ({ roleId: 'role_admin', permissionId: p.id })),\n ...SEED_PERMISSIONS.filter(p => complianceAllowed.has(p.name)).map(p => ({ roleId: 'role_compliance_officer', permissionId: p.id })),\n ...SEED_PERMISSIONS.filter(p => viewerAllowed.has(p.name)).map(p => ({ roleId: 'role_viewer', permissionId: p.id })),\n]\n\n// ── SQLite seed SQL ───────────────────────────────────────────────────────────\n// Generated from seed data above — idempotent via INSERT OR IGNORE.\n// MongoDB, MySQL, PostgreSQL adapters use the typed JS objects directly.\n\nfunction q(s: string): string { return `'${s.replace(/'/g, \"''\")}'` }\n\nfunction generateSeedSQL(): string {\n const { id, name, slug } = SEED_TENANT\n const tenant = `INSERT OR IGNORE INTO tenants (id, name, slug) VALUES (${q(id)},${q(name)},${q(slug)});`\n\n const perms = `INSERT OR IGNORE INTO permissions (id, name, description) VALUES\\n${\n SEED_PERMISSIONS.map(p => ` (${q(p.id)},${q(p.name)},${q(p.description)})`).join(',\\n')\n };`\n\n const rolesSQL = `INSERT OR IGNORE INTO roles (id, tenant_id, name, description) VALUES\\n${\n SEED_ROLES.map(r => ` (${q(r.id)},${q(r.tenantId)},${q(r.name)},${q(r.description)})`).join(',\\n')\n };`\n\n const byRole = new Map<string, string[]>()\n for (const rp of SEED_ROLE_PERMISSIONS) {\n if (!byRole.has(rp.roleId)) byRole.set(rp.roleId, [])\n byRole.get(rp.roleId)!.push(rp.permissionId)\n }\n const rolePerms = [...byRole.entries()].map(([roleId, permIds]) =>\n `INSERT OR IGNORE INTO role_permissions (role_id, permission_id) VALUES\\n${\n permIds.map(pid => ` (${q(roleId)},${q(pid)})`).join(',\\n')\n };`\n ).join('\\n')\n\n return [tenant, perms, rolesSQL, rolePerms].join('\\n')\n}\n\nexport const SEED_SQL = generateSeedSQL()\n","/**\n * Escapes SQL LIKE metacharacters (`%`, `_`, and the escape character itself) in user input so\n * it's matched literally, then appends a trailing `%` for prefix matching. Pair with\n * `ESCAPE '\\'` in the query.\n *\n * Deliberately prefix-only (`value%`), never `%value%`: a leading wildcard can't use a B-tree\n * index on any SQL dialect, forcing a full table scan. The columns this searches (visitor IDs,\n * profile IDs, locale/country codes, audit actions) are opaque IDs or short codes that users\n * paste or type from the start, not prose — prefix matching fits the data and stays index-backed.\n */\nexport function likePrefix(input: string): string {\n return `${input.replace(/[\\\\%_]/g, ch => `\\\\${ch}`)}%`\n}\n","import type { Database as DB, Statement } from 'better-sqlite3'\n\nconst MAX_PAGE_SIZE = 500\nimport { randomUUID, randomProfileId, randomVisitorId, randomConsentId, randomConsentTemplateId, randomUITemplateId } from '../../utils/crypto'\nimport { resolveStoragePaths } from '../../utils/storage-path'\nimport { SEED_SQL, SCHEMA_SQL_SQLITE } from '../seed-data'\nimport { likePrefix } from '../../utils/sql-search'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n StoredProfileJson,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentSummary,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditLogSummary,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n TenantSettings,\n ServerConsentTemplate,\n ServerUITemplate,\n CreateConsentTemplateInput,\n UpdateConsentTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n ProfileSummary,\n OptInStats,\n OptInFilters,\n ComplianceGroupId,\n NoticeShownRecord,\n CreateNoticeShownInput,\n PagedResult,\n} from '@consenti/types'\n\ninterface RawProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RawConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n signature?: string | null\n}\ninterface RawConsentSummary {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; gpc_detected: number; source: string\n age_verified?: number; created_at: string; updated_at: string\n}\ninterface RawConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RawVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RawNoticeShown {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; created_at: string\n}\ninterface RawUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n allowed_tenants?: string | null\n}\ninterface RawProfileSummary {\n id: string; name: string; default_locale: string\n compliance_group: string | null; custom_compliance_group: string | null; is_active: number\n consent_template_name: string | null; ui_template_name: string | null\n created_at: string; updated_at: string\n consent_template_id: string | null; ui_template_id: string | null\n}\ninterface RawRole {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RawPermission {\n id: string; name: string; description: string | null\n}\ninterface RawAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RawAuditLogSummary {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null; created_at: string\n}\ninterface RawCount { total: number }\ninterface RawTenant {\n id: string; name: string; slug: string; created_at: string; updated_at: string\n}\ninterface RawApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number\n created_by: string | null; expire_by: string | null; created_at: string; updated_at: string | null\n}\n\ninterface RawConsentTemplate {\n id: string; tenant_id: string; name: string; cookies_json: string; categories_json: string; created_at: string; updated_at: string\n}\ninterface RawUITemplate {\n id: string; tenant_id: string; name: string; settings_json: string; created_at: string; updated_at: string\n}\n\n\ntype Stmt = Statement\n\nexport class BetterSqlite3Adapter implements StorageAdapter {\n private db!: DB\n private cache = new Map<string, Stmt>()\n\n constructor(private config: StorageConfig) {}\n\n private stmt(sql: string): Stmt {\n let s = this.cache.get(sql)\n if (!s) { s = this.db.prepare(sql); this.cache.set(sql, s) }\n return s\n }\n\n async connect(): Promise<void> {\n // Lazy: only loaded when this driver is selected — keeps better-sqlite3\n // as an optional peer dep without crashing startup for other drivers.\n const mod = await import('better-sqlite3').catch(() => {\n throw new Error(\"driver 'better-sqlite3' requires: npm install better-sqlite3\")\n }) as { default: new(path: string) => DB }\n const { dbPath } = resolveStoragePaths(this.config.path ?? './consenti-data', 'better-sqlite3')\n this.db = new mod.default(dbPath)\n this.db.exec('PRAGMA journal_mode=WAL')\n this.db.exec('PRAGMA foreign_keys=ON')\n this.db.exec('PRAGMA synchronous=NORMAL')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n this.cache.clear()\n this.db.close()\n }\n\n async migrate(): Promise<void> {\n // No installations predate this schema, so a fresh connect always creates the full current\n // table set in one pass — no incremental/versioned steps needed. `PRAGMA user_version` is\n // kept as the hook for whenever a *real* future migration is needed: add `if (version < N)`\n // blocks below the initial creation, the same shape as the removed historical ones.\n const row = this.db.prepare('PRAGMA user_version').get() as { user_version: number }\n if (row.user_version < 1) {\n this.db.exec(SCHEMA_SQL_SQLITE)\n this.db.exec(SEED_SQL)\n this.db.exec('PRAGMA user_version = 1')\n }\n\n // Plain-column indexes (tenant_id, created_at, visitor_id, etc.) are generated into\n // SCHEMA_SQL_SQLITE from seed-data.ts's declarative `idx` field — see the `if` block above.\n // Only the JSON-expression index below survives here: SQLite's json_extract() functional\n // index has no equivalent representation across the Postgres/MySQL/Mongo dialects, so it\n // can't live in the shared declarative system.\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_profiles_tenant_compliance_active\n ON profiles (tenant_id, json_extract(profile_json,'$.complianceGroup'), json_extract(profile_json,'$.isActive'));\n `)\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n private mapProfile(row: RawProfile): Profile {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n defaultLocale: row.default_locale,\n version: row.version,\n profileJson: JSON.parse(row.profile_json) as StoredProfileJson,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO profiles (id,tenant_id,name,default_locale,version,profile_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.defaultLocale, 1, JSON.stringify(data.profileJson), now, now)\n const profile = await this.getProfile(id)\n if (!profile) throw new Error('Failed to create profile')\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const existing = await this.getProfile(id)\n if (!existing) throw new Error(`Profile ${id} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.defaultLocale != null) { fields.push('default_locale = ?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { fields.push('profile_json = ?'); vals.push(JSON.stringify(data.profileJson)) }\n if (data.version != null) { fields.push('version = ?'); vals.push(data.version) }\n vals.push(id)\n this.stmt(`UPDATE profiles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const updated = await this.getProfile(id)\n if (!updated) throw new Error('Failed to update profile')\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.stmt('DELETE FROM profiles WHERE id = ?').run(id)\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const row = this.stmt('SELECT * FROM profiles WHERE id = ?').get(id) as RawProfile | undefined\n return row ? this.mapProfile(row) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = this.stmt('SELECT * FROM profiles WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawProfile[]\n return rows.map(r => this.mapProfile(r))\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n const row = this.stmt(\n `SELECT * FROM profiles WHERE tenant_id = ?\n AND (json_extract(profile_json, '$.complianceGroup') = ? OR json_extract(profile_json, '$.customComplianceGroup') = ?)\n AND json_extract(profile_json, '$.isActive') = 1 LIMIT 1`\n ).get(tenantId, complianceGroup, complianceGroup) as RawProfile | undefined\n return row ? this.mapProfile(row) : null\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n private mapConsent(row: RawConsent): ConsentDbRecord {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n profileId: row.profile_id,\n locale: row.locale,\n consentJson: JSON.parse(row.consent_json) as ConsentValue,\n gpcDetected: row.gpc_detected === 1,\n source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n ...(row.parental_consent_token != null ? { parentalConsentToken: row.parental_consent_token } : {}),\n ...(row.tcf_string != null ? { tcfString: row.tcf_string } : {}),\n ...(row.signature != null ? { signature: row.signature } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n private static readonly CONSENT_SUMMARY_COLS =\n 'id, tenant_id, visitor_id, profile_id, locale, gpc_detected, source, age_verified, created_at, updated_at'\n\n private mapConsentSummary(row: RawConsentSummary): ConsentSummary {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id, profileId: row.profile_id,\n locale: row.locale, gpcDetected: row.gpc_detected === 1, source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomConsentId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO consent_records (id,tenant_id,visitor_id,profile_id,locale,consent_json,gpc_detected,source,age_verified,parental_consent_token,tcf_string,signature,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.profileId, data.locale, JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source, data.ageVerified ? 1 : 0, data.parentalConsentToken ?? null, data.tcfString ?? null, data.signature ?? null, now, now)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, data.tenantId, id, data.visitorId, null, JSON.stringify(data.consentJson), 'created', now)\n const record = await this.getConsent(data.visitorId)\n if (!record) throw new Error('Failed to create consent')\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'consent_json = ?']\n const vals: (string | number | null)[] = [now, JSON.stringify(data.consentJson)]\n if (data.locale != null) { fields.push('locale = ?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { fields.push('gpc_detected = ?'); vals.push(data.gpcDetected ? 1 : 0) }\n if (data.signature != null) { fields.push('signature = ?'); vals.push(data.signature) }\n vals.push(visitorId)\n this.stmt(`UPDATE consent_records SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, existing.tenantId, existing.id, visitorId, JSON.stringify(existing.consentJson), JSON.stringify(data.consentJson), 'updated', now)\n const updated = await this.getConsent(visitorId)\n if (!updated) throw new Error('Failed to update consent')\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitorId)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitorId)\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const row = this.stmt('SELECT * FROM consent_records WHERE visitor_id = ?').get(visitorId) as RawConsent | undefined\n return row ? this.mapConsent(row) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.profileId) { conditions.push('profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(visitor_id LIKE ? OR profile_id LIKE ? OR locale LIKE ? OR source LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like, like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT ${BetterSqlite3Adapter.CONSENT_SUMMARY_COLS} FROM consent_records WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, limit, offset) as unknown as RawConsentSummary[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM consent_records WHERE ${where}`).get(...vals) as RawCount\n return { items: rows.map(r => this.mapConsentSummary(r)), total: countRow.total, page, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n 'SELECT * FROM consent_records WHERE tenant_id = ? LIMIT ? OFFSET ?'\n ).all(filters.tenantId, pageSize, offset) as unknown as RawConsent[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapConsent(row)\n offset += pageSize\n }\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = this.stmt(\n 'SELECT * FROM consent_history WHERE visitor_id = ? ORDER BY created_at ASC'\n ).all(visitorId) as unknown as RawConsentHistory[]\n return rows.map(r => ({\n id: r.id,\n tenantId: r.tenant_id,\n consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json != null ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'],\n createdAt: r.created_at,\n }))\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO notice_shown (id,tenant_id,visitor_id,profile_id,locale,created_at) VALUES (?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.profileId, data.locale, now)\n return { id, tenantId: data.tenantId, visitorId: data.visitorId, profileId: data.profileId, locale: data.locale, createdAt: now }\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n const rows = this.stmt(\n 'SELECT * FROM notice_shown WHERE visitor_id = ? ORDER BY created_at DESC'\n ).all(visitorId) as unknown as RawNoticeShown[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n profileId: r.profile_id, locale: r.locale, createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n private mapVisitor(row: RawVisitor): Visitor {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n firstSeen: row.first_seen,\n lastSeen: row.last_seen,\n ...(row.country != null ? { country: row.country } : {}),\n ...(row.region != null ? { region: row.region } : {}),\n ...(row.city != null ? { city: row.city } : {}),\n ...(row.ip_hash != null ? { ipHash: row.ip_hash } : {}),\n ...(row.user_agent_hash != null ? { userAgentHash: row.user_agent_hash } : {}),\n }\n }\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomVisitorId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO visitors (id,tenant_id,visitor_id,country,region,city,ip_hash,user_agent_hash,first_seen,last_seen) VALUES (?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.country ?? null, data.region ?? null, data.city ?? null, data.ipHash ?? null, data.userAgentHash ?? null, now, now)\n const visitor = await this.getVisitor(data.visitorId)\n if (!visitor) throw new Error('Failed to create visitor')\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const fields: string[] = ['last_seen = ?']\n const vals: (string | number | null)[] = [data.lastSeen ?? now]\n if (data.country != null) { fields.push('country = ?'); vals.push(data.country) }\n if (data.region != null) { fields.push('region = ?'); vals.push(data.region) }\n if (data.city != null) { fields.push('city = ?'); vals.push(data.city) }\n vals.push(visitorId)\n this.stmt(`UPDATE visitors SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const visitor = await this.getVisitor(visitorId)\n if (!visitor) throw new Error(`Visitor ${visitorId} not found`)\n return visitor\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM visitors WHERE visitor_id = ?').run(visitorId)\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const row = this.stmt('SELECT * FROM visitors WHERE visitor_id = ?').get(visitorId) as RawVisitor | undefined\n return row ? this.mapVisitor(row) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.from) { conditions.push('first_seen >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('first_seen <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(visitor_id LIKE ? OR country LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT * FROM visitors WHERE ${where} ORDER BY last_seen DESC LIMIT ? OFFSET ?`\n ).all(...vals, limit, offset) as unknown as RawVisitor[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM visitors WHERE ${where}`).get(...vals) as RawCount\n return { items: rows.map(r => this.mapVisitor(r)), total: countRow.total, page, limit }\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────\n\n private mapUser(row: RawUser): AdminUser {\n const allowedTenants: string[] = row.allowed_tenants\n ? JSON.parse(row.allowed_tenants) as string[]\n : []\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n email: row.email,\n passwordHash: row.password_hash,\n isActive: row.is_active === 1,\n totpEnabled: row.totp_enabled === 1,\n ...(row.totp_secret != null ? { totpSecret: row.totp_secret } : {}),\n allowedTenants,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const allowedTenantsJson = data.allowedTenants?.length ? JSON.stringify(data.allowedTenants) : null\n this.stmt(\n 'INSERT INTO users (id,tenant_id,name,email,password_hash,allowed_tenants,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.email, data.passwordHash, allowedTenantsJson, now, now)\n const user = await this.getUserByEmail(data.email)\n if (!user) throw new Error('Failed to create user')\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.email != null) { fields.push('email = ?'); vals.push(data.email) }\n if (data.passwordHash != null) { fields.push('password_hash = ?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { fields.push('is_active = ?'); vals.push(data.isActive ? 1 : 0) }\n if ('totpSecret' in data) { fields.push('totp_secret = ?'); vals.push(data.totpSecret ?? null) }\n if (data.totpEnabled != null) { fields.push('totp_enabled = ?'); vals.push(data.totpEnabled ? 1 : 0) }\n if ('allowedTenants' in data) {\n fields.push('allowed_tenants = ?')\n vals.push(data.allowedTenants?.length ? JSON.stringify(data.allowedTenants) : null)\n }\n vals.push(id)\n this.stmt(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n if (!row) throw new Error(`User ${id} not found`)\n return this.mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n this.stmt('DELETE FROM user_roles WHERE user_id = ?').run(id)\n this.stmt('DELETE FROM users WHERE id = ?').run(id)\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE email = ?').get(email) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = this.stmt('SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawUser[]\n return rows.map(r => this.mapUser(r))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const row = tenantId\n ? this.stmt('SELECT COUNT(*) as n FROM users WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n : this.stmt('SELECT COUNT(*) as n FROM users').get() as { n: number } | undefined\n return row?.n ?? 0\n }\n\n // ── Roles & Permissions ───────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = this.stmt('SELECT * FROM roles WHERE tenant_id = ?').all(tenantId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n this.stmt('INSERT INTO roles (id,tenant_id,name,description) VALUES (?,?,?,?)').run(\n id, data.tenantId, data.name, data.description ?? null\n )\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error('Failed to create role')\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const fields: string[] = []\n const vals: (string | number | null)[] = []\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.description != null) { fields.push('description = ?'); vals.push(data.description) }\n if (fields.length > 0) {\n vals.push(id)\n this.stmt(`UPDATE roles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n }\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error(`Role ${id} not found`)\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async deleteRole(id: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM user_roles WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM roles WHERE id = ?').run(id)\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = this.stmt('SELECT * FROM permissions ORDER BY name ASC').all() as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)').run(roleId, permissionId)\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?').run(roleId, permissionId)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = this.stmt(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = ?'\n ).all(roleId) as unknown as RawPermission[]\n return rows.map(r => ({\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ? AND ur.tenant_id = ?'\n ).all(userId, tenantId) as unknown as RawRole[]\n : this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ?'\n ).all(userId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)').run(userId, roleId, tenantId)\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ? AND tenant_id = ?').run(userId, roleId, tenantId)\n } else {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?').run(userId, roleId)\n }\n }\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO audit_logs (id,tenant_id,user_id,action,resource_type,resource_id,old_data,new_data,created_at) VALUES (?,?,?,?,?,?,?,?,?)'\n ).run(\n id, data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null,\n now\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.resourceType) { conditions.push('resource_type = ?'); vals.push(filters.resourceType) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(action LIKE ? OR resource_type LIKE ? OR resource_id LIKE ? OR user_id LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like, like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT ${BetterSqlite3Adapter.AUDIT_SUMMARY_COLS} FROM audit_logs WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, limit, offset) as unknown as RawAuditLogSummary[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM audit_logs WHERE ${where}`).get(...vals) as RawCount\n return { items: rows.map(r => this.mapAuditSummary(r)), total: countRow.total, page, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n const row = this.stmt('SELECT * FROM audit_logs WHERE id = ?').get(id) as RawAuditLog | undefined\n return row ? this.mapAuditLog(row) : null\n }\n\n private static readonly AUDIT_SUMMARY_COLS = 'id, tenant_id, user_id, action, resource_type, resource_id, created_at'\n\n private mapAuditSummary(r: RawAuditLogSummary): AuditLogSummary {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n createdAt: r.created_at,\n }\n }\n\n private mapAuditLog(r: RawAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action,\n resourceType: r.resource_type, createdAt: r.created_at,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n }\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, pageSize, offset) as unknown as RawAuditLog[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapAuditLog(row)\n offset += pageSize\n }\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const tc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const gc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const vc = this.stmt('SELECT COUNT(*) as n FROM visitors WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const totalConsents = tc?.n ?? 0\n\n const recent = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 500'\n ).all(tenantId) as unknown as { consent_json: string }[]\n\n let accepted = 0, rejected = 0\n for (const r of recent) {\n const vals = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n\n return {\n totalConsents,\n gpcDetectedCount: gc?.n ?? 0,\n totalVisitors: vc?.n ?? 0,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? LIMIT 5000'\n ).all(tenantId) as unknown as { consent_json: string }[]\n const stats: CategoryStats = {}\n for (const row of rows) {\n const consent = JSON.parse(row.consent_json) as Record<string, string>\n for (const [key, status] of Object.entries(consent)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const rows = this.stmt(\n `SELECT date(created_at) as date, COUNT(*) as count FROM consent_records WHERE tenant_id = ? AND created_at >= date('now', ?) GROUP BY date(created_at) ORDER BY date ASC`\n ).all(tenantId, `-${days} days`) as unknown as { date: string; count: number }[]\n return rows.map(r => ({ date: r.date, count: r.count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = this.stmt(\n `SELECT v.country, COUNT(*) as count FROM consent_records cr JOIN visitors v ON v.visitor_id = cr.visitor_id WHERE cr.tenant_id = ? AND v.country IS NOT NULL GROUP BY v.country ORDER BY count DESC LIMIT 50`\n ).all(tenantId) as unknown as { country: string; count: number }[]\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const detected = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const t = total?.n ?? 0\n const d = detected?.n ?? 0\n return { detected: d, total: t, rate: t > 0 ? Math.round((d / t) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = this.stmt('SELECT * FROM tenants ORDER BY name ASC').all() as unknown as RawTenant[]\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO tenants (id,name,slug,created_at,updated_at) VALUES (?,?,?,?,?)').run(id, data.name, data.slug, now, now)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error('Failed to create tenant')\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.slug != null) { fields.push('slug = ?'); vals.push(data.slug) }\n vals.push(id)\n this.stmt(`UPDATE tenants SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error(`Tenant ${id} not found`)\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.stmt('DELETE FROM tenants WHERE id = ?').run(id)\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n const row = this.stmt('SELECT allowed_origins_json, admin_allowed_origins_json, setup_completed FROM tenant_settings WHERE tenant_id = ?')\n .get(tenantId) as { allowed_origins_json: string; admin_allowed_origins_json: string; setup_completed: number } | undefined\n return row\n ? {\n allowedOrigins: JSON.parse(row.allowed_origins_json) as string[],\n adminAllowedOrigins: JSON.parse(row.admin_allowed_origins_json) as string[],\n setupCompleted: !!row.setup_completed,\n }\n : {}\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const current = await this.getSettings(tenantId)\n const merged = { ...current, ...data }\n this.stmt(\n `INSERT INTO tenant_settings (tenant_id, allowed_origins_json, admin_allowed_origins_json, setup_completed, updated_at) VALUES (?,?,?,?,?)\n ON CONFLICT(tenant_id) DO UPDATE SET allowed_origins_json = excluded.allowed_origins_json, admin_allowed_origins_json = excluded.admin_allowed_origins_json, setup_completed = excluded.setup_completed, updated_at = excluded.updated_at`,\n ).run(tenantId, JSON.stringify(merged.allowedOrigins ?? []), JSON.stringify(merged.adminAllowedOrigins ?? []), merged.setupCompleted ? 1 : 0, new Date().toISOString())\n return this.getSettings(tenantId)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = this.stmt(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?'\n ).all(cutoff) as unknown as { visitor_id: string }[]\n for (const { visitor_id } of toDelete) {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitor_id)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitor_id)\n }\n return toDelete.length\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const { count } = this.stmt('SELECT COUNT(*) AS count FROM audit_logs WHERE created_at < ?').get(cutoff) as { count: number }\n this.stmt('DELETE FROM audit_logs WHERE created_at < ?').run(cutoff)\n return count\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RawApiKey): ApiKey {\n return {\n id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1,\n ...(r.created_by !== null ? { createdBy: r.created_by } : {}),\n ...(r.expire_by !== null ? { expireBy: r.expire_by } : {}),\n createdAt: r.created_at,\n ...(r.updated_at !== null ? { updatedAt: r.updated_at } : {}),\n }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO api_keys (id,tenant_id,key_hash,name,created_by,expire_by,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)')\n .run(id, data.tenantId, data.keyHash, data.name, data.createdBy ?? null, data.expireBy ?? null, now, now)\n const row = this.stmt('SELECT * FROM api_keys WHERE id = ?').get(id) as RawApiKey | undefined\n if (!row) throw new Error('Failed to create API key')\n return this.mapApiKey(row)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const row = this.stmt('SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) as RawApiKey | undefined\n return row ? this.mapApiKey(row) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n this.stmt('UPDATE api_keys SET is_active = 0, updated_at = ? WHERE id = ?').run(new Date().toISOString(), id)\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n const now = new Date().toISOString()\n this.stmt(\n 'UPDATE api_keys SET is_active = 1, updated_at = ?, expire_by = CASE WHEN expire_by <= ? THEN NULL ELSE expire_by END WHERE id = ?',\n ).run(now, now, id)\n }\n\n async deleteApiKey(id: string): Promise<void> {\n this.stmt('DELETE FROM api_keys WHERE id = ?').run(id)\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = this.stmt('SELECT * FROM api_keys WHERE tenant_id = ? ORDER BY created_at DESC').all(tenantId) as unknown as RawApiKey[]\n return rows.map(r => this.mapApiKey(r))\n }\n\n // ── Consent Templates ──────────────────────────────────────────────────────\n\n private mapConsentTemplate(r: RawConsentTemplate): ServerConsentTemplate {\n return { id: r.id, tenantId: r.tenant_id, name: r.name, cookies: JSON.parse(r.cookies_json), categories: JSON.parse(r.categories_json), createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createConsentTemplate(data: CreateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const id = randomConsentTemplateId()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO consent_templates (id,tenant_id,name,cookies_json,categories_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?)').run(id, data.tenantId, data.name, JSON.stringify(data.cookies), JSON.stringify(data.categories), now, now)\n return this.mapConsentTemplate(this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as unknown as RawConsentTemplate)\n }\n\n async updateConsentTemplate(id: string, data: UpdateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const now = new Date().toISOString()\n if (data.name !== undefined) this.stmt('UPDATE consent_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n if (data.cookies !== undefined) this.stmt('UPDATE consent_templates SET cookies_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(data.cookies), now, id)\n if (data.categories !== undefined) this.stmt('UPDATE consent_templates SET categories_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(data.categories), now, id)\n const row = this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as RawConsentTemplate | undefined\n if (!row) throw new Error('Consent template not found')\n return this.mapConsentTemplate(row)\n }\n\n async deleteConsentTemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM consent_templates WHERE id = ?').run(id)\n }\n\n async getConsentTemplate(id: string): Promise<ServerConsentTemplate | null> {\n const row = this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as RawConsentTemplate | undefined\n return row ? this.mapConsentTemplate(row) : null\n }\n\n async getConsentTemplates(tenantId: string): Promise<ServerConsentTemplate[]> {\n const rows = this.stmt('SELECT * FROM consent_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawConsentTemplate[]\n return rows.map(r => this.mapConsentTemplate(r))\n }\n\n async copyConsentTemplate(id: string, newName: string): Promise<ServerConsentTemplate> {\n const src = await this.getConsentTemplate(id)\n if (!src) throw new Error('Consent template not found')\n return this.createConsentTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies, categories: src.categories })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n private mapUITemplate(r: RawUITemplate): ServerUITemplate {\n const s = JSON.parse(r.settings_json) as Omit<ServerUITemplate, 'id' | 'tenantId' | 'name' | 'createdAt' | 'updatedAt'>\n return { id: r.id, tenantId: r.tenant_id, name: r.name, ...s, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const id = randomUITemplateId()\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n this.stmt('INSERT INTO ui_templates (id,tenant_id,name,settings_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run(id, tenantId, name, JSON.stringify(settings), now, now)\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n if (!row) throw new Error('UI template not found')\n if (data.name !== undefined) this.stmt('UPDATE ui_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n const { name: _n, ...settingsUpdate } = data\n if (Object.keys(settingsUpdate).length > 0) {\n const current = JSON.parse(row.settings_json) as Record<string, unknown>\n const merged = { ...current, ...settingsUpdate }\n this.stmt('UPDATE ui_templates SET settings_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(merged), now, id)\n }\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM ui_templates WHERE id = ?').run(id)\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n return row ? this.mapUITemplate(row) : null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n const rows = this.stmt('SELECT * FROM ui_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawUITemplate[]\n return rows.map(r => this.mapUITemplate(r))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src\n return this.createUITemplate({ tenantId, name: newName, mainBanner, gpcBanner, preferenceModal })\n }\n\n // ── Profile summaries ──────────────────────────────────────────────────────\n\n private mapProfileSummary(r: RawProfileSummary): ProfileSummary {\n return {\n id: r.id,\n name: r.name,\n defaultLocale: r.default_locale,\n complianceGroup: (r.compliance_group ?? null) as ComplianceGroupId | null,\n customComplianceGroup: r.custom_compliance_group ?? null,\n isActive: r.is_active === 1,\n consentTemplateName: r.consent_template_name,\n uiTemplateName: r.ui_template_name,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n }\n\n private readonly PROFILE_SUMMARY_SQL = `\n SELECT\n p.id, p.name, p.default_locale, p.created_at, p.updated_at,\n json_extract(p.profile_json, '$.complianceGroup') AS compliance_group,\n json_extract(p.profile_json, '$.customComplianceGroup') AS custom_compliance_group,\n json_extract(p.profile_json, '$.isActive') AS is_active,\n json_extract(p.profile_json, '$.consentTemplateId') AS consent_template_id,\n json_extract(p.profile_json, '$.uiTemplateId') AS ui_template_id,\n ct.name AS consent_template_name,\n ut.name AS ui_template_name\n FROM profiles p\n LEFT JOIN consent_templates ct ON ct.id = json_extract(p.profile_json, '$.consentTemplateId')\n LEFT JOIN ui_templates ut ON ut.id = json_extract(p.profile_json, '$.uiTemplateId')\n `\n\n async listProfilesSummary(tenantId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE p.tenant_id = ? ORDER BY p.created_at ASC`\n ).all(tenantId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async findProfilesUsingConsentTemplate(templateId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE json_extract(p.profile_json, '$.consentTemplateId') = ? ORDER BY p.name ASC`\n ).all(templateId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async findProfilesUsingUITemplate(templateId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE json_extract(p.profile_json, '$.uiTemplateId') = ? ORDER BY p.name ASC`\n ).all(templateId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async getOptInStats(tenantId: string, filters: OptInFilters): Promise<OptInStats> {\n const conditions: string[] = ['cr.tenant_id = ?']\n const vals: (string | number | null)[] = [tenantId]\n if (filters.profileId) { conditions.push('cr.profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('cr.created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('cr.created_at <= ?'); vals.push(filters.to) }\n if (filters.locale) { conditions.push('cr.locale = ?'); vals.push(filters.locale) }\n const where = conditions.join(' AND ')\n\n const rows = this.stmt(\n `SELECT cr.locale, cr.consent_json FROM consent_records cr WHERE ${where}`\n ).all(...vals) as unknown as { locale: string; consent_json: string }[]\n\n let total = 0, granted = 0, denied = 0, managed = 0\n const byLocale: Record<string, { total: number; granted: number; denied: number; managed: number }> = {}\n\n for (const r of rows) {\n const statuses = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = statuses.length > 0 ? statuses.filter(s => s === 'granted').length / statuses.length : 0\n const deniedPct = statuses.length > 0 ? statuses.filter(s => s === 'denied').length / statuses.length : 0\n total++\n const loc = r.locale\n if (!byLocale[loc]) byLocale[loc] = { total: 0, granted: 0, denied: 0, managed: 0 }\n byLocale[loc]!.total++\n if (grantedPct >= 0.9) { granted++; byLocale[loc]!.granted++ }\n else if (deniedPct >= 0.9) { denied++; byLocale[loc]!.denied++ }\n else { managed++; byLocale[loc]!.managed++ }\n }\n\n const dateRows = this.stmt(\n `SELECT date(cr.created_at) as d, cr.consent_json FROM consent_records cr WHERE ${where} ORDER BY d ASC`\n ).all(...vals) as unknown as { d: string; consent_json: string }[]\n\n const dateMap = new Map<string, { total: number; granted: number; denied: number; managed: number }>()\n for (const r of dateRows) {\n const entry = dateMap.get(r.d) ?? { total: 0, granted: 0, denied: 0, managed: 0 }\n const statuses = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = statuses.length > 0 ? statuses.filter(s => s === 'granted').length / statuses.length : 0\n const deniedPct = statuses.length > 0 ? statuses.filter(s => s === 'denied').length / statuses.length : 0\n entry.total++\n if (grantedPct >= 0.9) entry.granted++\n else if (deniedPct >= 0.9) entry.denied++\n else entry.managed++\n dateMap.set(r.d, entry)\n }\n\n const byDate = Array.from(dateMap.entries()).map(([date, v]) => ({ date, ...v }))\n return {\n total, granted, denied, managed,\n grantedPct: total > 0 ? Math.round((granted / total) * 1000) / 10 : 0,\n deniedPct: total > 0 ? Math.round((denied / total) * 1000) / 10 : 0,\n managedPct: total > 0 ? Math.round((managed / total) * 1000) / 10 : 0,\n byLocale,\n byDate,\n }\n }\n}\n","import type { Database as DB, Statement } from 'node-sqlite3-wasm'\n\nconst MAX_PAGE_SIZE = 500\nimport { randomUUID, randomProfileId, randomVisitorId, randomConsentId, randomConsentTemplateId, randomUITemplateId } from '../../utils/crypto'\nimport { resolveStoragePaths } from '../../utils/storage-path'\nimport { SEED_SQL, SCHEMA_SQL_SQLITE } from '../seed-data'\nimport { likePrefix } from '../../utils/sql-search'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n StoredProfileJson,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentSummary,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditLogSummary,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n TenantSettings,\n ServerConsentTemplate,\n ServerUITemplate,\n CreateConsentTemplateInput,\n UpdateConsentTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n ProfileSummary,\n OptInStats,\n OptInFilters,\n ComplianceGroupId,\n NoticeShownRecord,\n CreateNoticeShownInput,\n PagedResult,\n} from '@consenti/types'\n\ninterface RawProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RawConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n signature?: string | null\n}\ninterface RawConsentSummary {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; gpc_detected: number; source: string\n age_verified?: number; created_at: string; updated_at: string\n}\ninterface RawConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RawVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RawNoticeShown {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; created_at: string\n}\ninterface RawUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n allowed_tenants?: string | null\n}\ninterface RawProfileSummary {\n id: string; name: string; default_locale: string\n compliance_group: string | null; custom_compliance_group: string | null; is_active: number\n consent_template_name: string | null; ui_template_name: string | null\n created_at: string; updated_at: string\n}\ninterface RawRole {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RawPermission {\n id: string; name: string; description: string | null\n}\ninterface RawAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RawAuditLogSummary {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null; created_at: string\n}\ninterface RawCount { total: number }\ninterface RawTenant {\n id: string; name: string; slug: string; created_at: string; updated_at: string\n}\ninterface RawApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number\n created_by: string | null; expire_by: string | null; created_at: string; updated_at: string | null\n}\ninterface RawConsentTemplate {\n id: string; tenant_id: string; name: string; cookies_json: string; categories_json: string; created_at: string; updated_at: string\n}\ninterface RawUITemplate {\n id: string; tenant_id: string; name: string; settings_json: string; created_at: string; updated_at: string\n}\n\n\n// node-sqlite3-wasm statements are NOT garbage-collected — they must be\n// finalized explicitly. All cached statements are finalized in disconnect().\n// Parameter binding uses run(array) / all(array) — not spread — per the WASM API.\n\ntype Val = string | number | null\n\nexport class NodeSqlite3WasmAdapter implements StorageAdapter {\n private db!: DB\n private cache = new Map<string, Statement>()\n\n constructor(private config: StorageConfig) { }\n\n private stmt(sql: string): Statement {\n let s = this.cache.get(sql)\n if (!s) { s = this.db.prepare(sql); this.cache.set(sql, s) }\n return s\n }\n\n async connect(): Promise<void> {\n // Lazy: only loaded when this driver is actually selected — allows the package\n // to be an optional peer dep without crashing on startup for other drivers.\n const mod = await import('node-sqlite3-wasm').catch(() => {\n throw new Error(\"driver 'node-sqlite3-wasm' requires: npm install node-sqlite3-wasm\")\n }) as { default: { Database: new (path: string) => DB } }\n const { dbPath } = resolveStoragePaths(this.config.path ?? './consenti-data', 'node-sqlite3-wasm')\n this.db = new mod.default.Database(dbPath)\n this.db.exec('PRAGMA journal_mode=WAL')\n this.db.exec('PRAGMA foreign_keys=ON')\n this.db.exec('PRAGMA synchronous=NORMAL')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n // Finalize all prepared statements before closing — WASM objects are not\n // GC'd and will leak if not explicitly freed.\n for (const stmt of this.cache.values()) {\n stmt.finalize()\n }\n this.cache.clear()\n this.db.close()\n }\n\n async migrate(): Promise<void> {\n // No installations predate this schema, so a fresh connect always creates the full current\n // table set in one pass — no incremental/versioned steps needed. `PRAGMA user_version` is\n // kept as the hook for whenever a *real* future migration is needed: add `if (ver < N)`\n // blocks below the initial creation, the same shape as the removed historical ones.\n const row = this.stmt('PRAGMA user_version').get() as { user_version: number } | null\n const ver = row?.user_version ?? 0\n if (ver < 1) {\n this.db.exec(SCHEMA_SQL_SQLITE)\n this.db.exec(SEED_SQL)\n this.db.exec('PRAGMA user_version = 1')\n }\n\n // Plain-column indexes (tenant_id, created_at, visitor_id, etc.) are generated into\n // SCHEMA_SQL_SQLITE from seed-data.ts's declarative `idx` field — see the `if` block above.\n // Only the JSON-expression index below survives here: SQLite's json_extract() functional\n // index has no equivalent representation across the Postgres/MySQL/Mongo dialects, so it\n // can't live in the shared declarative system.\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_profiles_tenant_compliance_active\n ON profiles (tenant_id, json_extract(profile_json,'$.complianceGroup'), json_extract(profile_json,'$.isActive'));\n `)\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n private mapProfile(row: RawProfile): Profile {\n return {\n id: row.id, tenantId: row.tenant_id, name: row.name,\n defaultLocale: row.default_locale, version: row.version,\n profileJson: JSON.parse(row.profile_json) as StoredProfileJson,\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO profiles (id,tenant_id,name,default_locale,version,profile_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.name, data.defaultLocale, 1, JSON.stringify(data.profileJson), now, now])\n const profile = await this.getProfile(id)\n if (!profile) throw new Error('Failed to create profile')\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const existing = await this.getProfile(id)\n if (!existing) throw new Error(`Profile ${id} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: Val[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.defaultLocale != null) { fields.push('default_locale = ?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { fields.push('profile_json = ?'); vals.push(JSON.stringify(data.profileJson)) }\n if (data.version != null) { fields.push('version = ?'); vals.push(data.version) }\n vals.push(id)\n this.stmt(`UPDATE profiles SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n const updated = await this.getProfile(id)\n if (!updated) throw new Error('Failed to update profile')\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.stmt('DELETE FROM profiles WHERE id = ?').run(id)\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const row = this.stmt('SELECT * FROM profiles WHERE id = ?').get(id) as RawProfile | null\n return row ? this.mapProfile(row) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = this.stmt('SELECT * FROM profiles WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawProfile[]\n return rows.map(r => this.mapProfile(r))\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n // Fetch all tenant profiles and filter in memory — wasm Statement.get() only accepts a single bind value\n const profiles = await this.getProfiles(tenantId)\n return profiles.find(p =>\n (p.profileJson.complianceGroup === complianceGroup || (p.profileJson as { customComplianceGroup?: string }).customComplianceGroup === complianceGroup) &&\n p.profileJson.isActive\n ) ?? null\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n private mapConsent(row: RawConsent): ConsentDbRecord {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id,\n profileId: row.profile_id,\n locale: row.locale, consentJson: JSON.parse(row.consent_json) as ConsentValue,\n gpcDetected: row.gpc_detected === 1,\n source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n ...(row.parental_consent_token != null ? { parentalConsentToken: row.parental_consent_token } : {}),\n ...(row.tcf_string != null ? { tcfString: row.tcf_string } : {}),\n ...(row.signature != null ? { signature: row.signature } : {}),\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n private static readonly CONSENT_SUMMARY_COLS =\n 'id, tenant_id, visitor_id, profile_id, locale, gpc_detected, source, age_verified, created_at, updated_at'\n\n private mapConsentSummary(row: RawConsentSummary): ConsentSummary {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id, profileId: row.profile_id,\n locale: row.locale, gpcDetected: row.gpc_detected === 1, source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomConsentId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO consent_records (id,tenant_id,visitor_id,profile_id,locale,consent_json,gpc_detected,source,age_verified,parental_consent_token,tcf_string,signature,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.visitorId, data.profileId, data.locale, JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source, data.ageVerified ? 1 : 0, data.parentalConsentToken ?? null, data.tcfString ?? null, data.signature ?? null, now, now])\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run([histId, data.tenantId, id, data.visitorId, null, JSON.stringify(data.consentJson), 'created', now])\n const record = await this.getConsent(data.visitorId)\n if (!record) throw new Error('Failed to create consent')\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'consent_json = ?']\n const vals: Val[] = [now, JSON.stringify(data.consentJson)]\n if (data.locale != null) { fields.push('locale = ?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { fields.push('gpc_detected = ?'); vals.push(data.gpcDetected ? 1 : 0) }\n if (data.signature != null) { fields.push('signature = ?'); vals.push(data.signature) }\n vals.push(visitorId)\n this.stmt(`UPDATE consent_records SET ${fields.join(', ')} WHERE visitor_id = ?`).run(vals)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run([histId, existing.tenantId, existing.id, visitorId, JSON.stringify(existing.consentJson), JSON.stringify(data.consentJson), 'updated', now])\n const updated = await this.getConsent(visitorId)\n if (!updated) throw new Error('Failed to update consent')\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitorId)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitorId)\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const row = this.stmt('SELECT * FROM consent_records WHERE visitor_id = ?').get(visitorId) as RawConsent | null\n return row ? this.mapConsent(row) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.profileId) { conditions.push('profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(visitor_id LIKE ? OR profile_id LIKE ? OR locale LIKE ? OR source LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like, like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT ${NodeSqlite3WasmAdapter.CONSENT_SUMMARY_COLS} FROM consent_records WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all([...vals, limit, offset]) as unknown as RawConsentSummary[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM consent_records WHERE ${where}`).get(vals) as unknown as RawCount | null\n return { items: rows.map(r => this.mapConsentSummary(r)), total: countRow?.total ?? 0, page, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n 'SELECT * FROM consent_records WHERE tenant_id = ? LIMIT ? OFFSET ?'\n ).all([filters.tenantId, pageSize, offset]) as unknown as RawConsent[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapConsent(row)\n offset += pageSize\n }\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = this.stmt(\n 'SELECT * FROM consent_history WHERE visitor_id = ? ORDER BY created_at ASC'\n ).all(visitorId) as unknown as RawConsentHistory[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json != null ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'],\n createdAt: r.created_at,\n }))\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO notice_shown (id,tenant_id,visitor_id,profile_id,locale,created_at) VALUES (?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.visitorId, data.profileId, data.locale, now])\n return { id, tenantId: data.tenantId, visitorId: data.visitorId, profileId: data.profileId, locale: data.locale, createdAt: now }\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n const rows = this.stmt(\n 'SELECT * FROM notice_shown WHERE visitor_id = ? ORDER BY created_at DESC'\n ).all(visitorId) as unknown as RawNoticeShown[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n profileId: r.profile_id, locale: r.locale, createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n private mapVisitor(row: RawVisitor): Visitor {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id,\n firstSeen: row.first_seen, lastSeen: row.last_seen,\n ...(row.country != null ? { country: row.country } : {}),\n ...(row.region != null ? { region: row.region } : {}),\n ...(row.city != null ? { city: row.city } : {}),\n ...(row.ip_hash != null ? { ipHash: row.ip_hash } : {}),\n ...(row.user_agent_hash != null ? { userAgentHash: row.user_agent_hash } : {}),\n }\n }\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomVisitorId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO visitors (id,tenant_id,visitor_id,country,region,city,ip_hash,user_agent_hash,first_seen,last_seen) VALUES (?,?,?,?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.visitorId, data.country ?? null, data.region ?? null, data.city ?? null, data.ipHash ?? null, data.userAgentHash ?? null, now, now])\n const visitor = await this.getVisitor(data.visitorId)\n if (!visitor) throw new Error('Failed to create visitor')\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const fields: string[] = ['last_seen = ?']\n const vals: Val[] = [data.lastSeen ?? now]\n if (data.country != null) { fields.push('country = ?'); vals.push(data.country) }\n if (data.region != null) { fields.push('region = ?'); vals.push(data.region) }\n if (data.city != null) { fields.push('city = ?'); vals.push(data.city) }\n vals.push(visitorId)\n this.stmt(`UPDATE visitors SET ${fields.join(', ')} WHERE visitor_id = ?`).run(vals)\n const visitor = await this.getVisitor(visitorId)\n if (!visitor) throw new Error(`Visitor ${visitorId} not found`)\n return visitor\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM visitors WHERE visitor_id = ?').run(visitorId)\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const row = this.stmt('SELECT * FROM visitors WHERE visitor_id = ?').get(visitorId) as RawVisitor | null\n return row ? this.mapVisitor(row) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.from) { conditions.push('first_seen >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('first_seen <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(visitor_id LIKE ? OR country LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT * FROM visitors WHERE ${where} ORDER BY last_seen DESC LIMIT ? OFFSET ?`\n ).all([...vals, limit, offset]) as unknown as RawVisitor[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM visitors WHERE ${where}`).get(vals) as unknown as RawCount | null\n return { items: rows.map(r => this.mapVisitor(r)), total: countRow?.total ?? 0, page, limit }\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────\n\n private mapUser(row: RawUser): AdminUser {\n const allowedTenants: string[] = row.allowed_tenants\n ? JSON.parse(row.allowed_tenants) as string[]\n : []\n return {\n id: row.id, tenantId: row.tenant_id, name: row.name, email: row.email,\n passwordHash: row.password_hash, isActive: row.is_active === 1,\n totpEnabled: row.totp_enabled === 1,\n ...(row.totp_secret != null ? { totpSecret: row.totp_secret } : {}),\n allowedTenants,\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const allowedTenantsJson = data.allowedTenants?.length ? JSON.stringify(data.allowedTenants) : null\n this.stmt(\n 'INSERT INTO users (id,tenant_id,name,email,password_hash,allowed_tenants,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run([id, data.tenantId, data.name, data.email, data.passwordHash, allowedTenantsJson, now, now])\n const user = await this.getUserByEmail(data.email)\n if (!user) throw new Error('Failed to create user')\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: Val[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.email != null) { fields.push('email = ?'); vals.push(data.email) }\n if (data.passwordHash != null) { fields.push('password_hash = ?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { fields.push('is_active = ?'); vals.push(data.isActive ? 1 : 0) }\n if ('totpSecret' in data) { fields.push('totp_secret = ?'); vals.push(data.totpSecret ?? null) }\n if (data.totpEnabled != null) { fields.push('totp_enabled = ?'); vals.push(data.totpEnabled ? 1 : 0) }\n if ('allowedTenants' in data) {\n fields.push('allowed_tenants = ?')\n vals.push(data.allowedTenants?.length ? JSON.stringify(data.allowedTenants) : null)\n }\n vals.push(id)\n this.stmt(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | null\n if (!row) throw new Error(`User ${id} not found`)\n return this.mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n this.stmt('DELETE FROM user_roles WHERE user_id = ?').run(id)\n this.stmt('DELETE FROM users WHERE id = ?').run(id)\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | null\n return row ? this.mapUser(row) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE email = ?').get(email) as RawUser | null\n return row ? this.mapUser(row) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = this.stmt('SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawUser[]\n return rows.map(r => this.mapUser(r))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const row = tenantId\n ? this.stmt('SELECT COUNT(*) as n FROM users WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n : this.stmt('SELECT COUNT(*) as n FROM users').get() as { n: number } | null\n return row?.n ?? 0\n }\n\n // ── Roles & Permissions ───────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = this.stmt('SELECT * FROM roles WHERE tenant_id = ?').all(tenantId) as unknown as RawRole[]\n return rows.map(r => ({ id: r.id, tenantId: r.tenant_id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n this.stmt('INSERT INTO roles (id,tenant_id,name,description) VALUES (?,?,?,?)').run(\n [id, data.tenantId, data.name, data.description ?? null]\n )\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | null\n if (!row) throw new Error('Failed to create role')\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const fields: string[] = []\n const vals: Val[] = []\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.description != null) { fields.push('description = ?'); vals.push(data.description) }\n if (fields.length > 0) {\n vals.push(id)\n this.stmt(`UPDATE roles SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n }\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | null\n if (!row) throw new Error(`Role ${id} not found`)\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async deleteRole(id: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM user_roles WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM roles WHERE id = ?').run(id)\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = this.stmt('SELECT * FROM permissions ORDER BY name ASC').all() as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)').run([roleId, permissionId])\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?').run([roleId, permissionId])\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = this.stmt(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = ?'\n ).all(roleId) as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ? AND ur.tenant_id = ?'\n ).all([userId, tenantId]) as unknown as RawRole[]\n : this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ?'\n ).all(userId) as unknown as RawRole[]\n return rows.map(r => ({ id: r.id, tenantId: r.tenant_id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)').run([userId, roleId, tenantId])\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ? AND tenant_id = ?').run([userId, roleId, tenantId])\n } else {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?').run([userId, roleId])\n }\n }\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO audit_logs (id,tenant_id,user_id,action,resource_type,resource_id,old_data,new_data,created_at) VALUES (?,?,?,?,?,?,?,?,?)'\n ).run([\n id, data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null,\n now,\n ])\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.resourceType) { conditions.push('resource_type = ?'); vals.push(filters.resourceType) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(action LIKE ? OR resource_type LIKE ? OR resource_id LIKE ? OR user_id LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like, like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT ${NodeSqlite3WasmAdapter.AUDIT_SUMMARY_COLS} FROM audit_logs WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all([...vals, limit, offset]) as unknown as RawAuditLogSummary[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM audit_logs WHERE ${where}`).get(vals) as unknown as RawCount | null\n return { items: rows.map(r => this.mapAuditSummary(r)), total: countRow?.total ?? 0, page, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n const row = this.stmt('SELECT * FROM audit_logs WHERE id = ?').get(id) as RawAuditLog | null\n return row ? this.mapAuditLog(row) : null\n }\n\n private static readonly AUDIT_SUMMARY_COLS = 'id, tenant_id, user_id, action, resource_type, resource_id, created_at'\n\n private mapAuditSummary(r: RawAuditLogSummary): AuditLogSummary {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n createdAt: r.created_at,\n }\n }\n\n private mapAuditLog(r: RawAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action,\n resourceType: r.resource_type, createdAt: r.created_at,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n }\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: Val[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all([...vals, pageSize, offset]) as unknown as RawAuditLog[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapAuditLog(row)\n offset += pageSize\n }\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const tc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n const gc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | null\n const vc = this.stmt('SELECT COUNT(*) as n FROM visitors WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n const totalConsents = tc?.n ?? 0\n\n const recent = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 500'\n ).all(tenantId) as unknown as { consent_json: string }[]\n\n let accepted = 0, rejected = 0\n for (const r of recent) {\n const vals = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n\n return {\n totalConsents,\n gpcDetectedCount: gc?.n ?? 0,\n totalVisitors: vc?.n ?? 0,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? LIMIT 5000'\n ).all(tenantId) as unknown as { consent_json: string }[]\n const stats: CategoryStats = {}\n for (const row of rows) {\n const consent = JSON.parse(row.consent_json) as Record<string, string>\n for (const [key, status] of Object.entries(consent)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const rows = this.stmt(\n `SELECT date(created_at) as date, COUNT(*) as count FROM consent_records WHERE tenant_id = ? AND created_at >= date('now', ?) GROUP BY date(created_at) ORDER BY date ASC`\n ).all([tenantId, `-${days} days`]) as unknown as { date: string; count: number }[]\n return rows.map(r => ({ date: r.date, count: r.count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = this.stmt(\n `SELECT v.country, COUNT(*) as count FROM consent_records cr JOIN visitors v ON v.visitor_id = cr.visitor_id WHERE cr.tenant_id = ? AND v.country IS NOT NULL GROUP BY v.country ORDER BY count DESC LIMIT 50`\n ).all(tenantId) as unknown as { country: string; count: number }[]\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | null\n const detected = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | null\n const t = total?.n ?? 0\n const d = detected?.n ?? 0\n return { detected: d, total: t, rate: t > 0 ? Math.round((d / t) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = this.stmt('SELECT * FROM tenants ORDER BY name ASC').all() as unknown as RawTenant[]\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO tenants (id,name,slug,created_at,updated_at) VALUES (?,?,?,?,?)').run([id, data.name, data.slug, now, now])\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | null\n if (!row) throw new Error('Failed to create tenant')\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: Val[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.slug != null) { fields.push('slug = ?'); vals.push(data.slug) }\n vals.push(id)\n this.stmt(`UPDATE tenants SET ${fields.join(', ')} WHERE id = ?`).run(vals)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | null\n if (!row) throw new Error(`Tenant ${id} not found`)\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.stmt('DELETE FROM tenants WHERE id = ?').run(id)\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n const row = this.stmt('SELECT allowed_origins_json, admin_allowed_origins_json, setup_completed FROM tenant_settings WHERE tenant_id = ?')\n .get(tenantId) as { allowed_origins_json: string; admin_allowed_origins_json: string; setup_completed: number } | null\n return row\n ? {\n allowedOrigins: JSON.parse(row.allowed_origins_json) as string[],\n adminAllowedOrigins: JSON.parse(row.admin_allowed_origins_json) as string[],\n setupCompleted: !!row.setup_completed,\n }\n : {}\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const current = await this.getSettings(tenantId)\n const merged = { ...current, ...data }\n this.stmt(\n `INSERT INTO tenant_settings (tenant_id, allowed_origins_json, admin_allowed_origins_json, setup_completed, updated_at) VALUES (?,?,?,?,?)\n ON CONFLICT(tenant_id) DO UPDATE SET allowed_origins_json = excluded.allowed_origins_json, admin_allowed_origins_json = excluded.admin_allowed_origins_json, setup_completed = excluded.setup_completed, updated_at = excluded.updated_at`,\n ).run([tenantId, JSON.stringify(merged.allowedOrigins ?? []), JSON.stringify(merged.adminAllowedOrigins ?? []), merged.setupCompleted ? 1 : 0, new Date().toISOString()])\n return this.getSettings(tenantId)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = this.stmt(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?'\n ).all(cutoff) as unknown as { visitor_id: string }[]\n for (const { visitor_id } of toDelete) {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitor_id)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitor_id)\n }\n return toDelete.length\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const { count } = this.stmt('SELECT COUNT(*) AS count FROM audit_logs WHERE created_at < ?').get(cutoff) as { count: number }\n this.stmt('DELETE FROM audit_logs WHERE created_at < ?').run(cutoff)\n return count\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RawApiKey): ApiKey {\n return {\n id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1,\n ...(r.created_by !== null ? { createdBy: r.created_by } : {}),\n ...(r.expire_by !== null ? { expireBy: r.expire_by } : {}),\n createdAt: r.created_at,\n ...(r.updated_at !== null ? { updatedAt: r.updated_at } : {}),\n }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO api_keys (id,tenant_id,key_hash,name,created_by,expire_by,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)')\n .run([id, data.tenantId, data.keyHash, data.name, data.createdBy ?? null, data.expireBy ?? null, now, now])\n const row = this.stmt('SELECT * FROM api_keys WHERE id = ?').get(id) as RawApiKey | null\n if (!row) throw new Error('Failed to create API key')\n return this.mapApiKey(row)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const row = this.stmt('SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) as RawApiKey | null\n return row ? this.mapApiKey(row) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n this.stmt('UPDATE api_keys SET is_active = 0, updated_at = ? WHERE id = ?').run([new Date().toISOString(), id])\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n const now = new Date().toISOString()\n this.stmt(\n 'UPDATE api_keys SET is_active = 1, updated_at = ?, expire_by = CASE WHEN expire_by <= ? THEN NULL ELSE expire_by END WHERE id = ?',\n ).run([now, now, id])\n }\n\n async deleteApiKey(id: string): Promise<void> {\n this.stmt('DELETE FROM api_keys WHERE id = ?').run([id])\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = this.stmt('SELECT * FROM api_keys WHERE tenant_id = ? ORDER BY created_at DESC').all(tenantId) as unknown as RawApiKey[]\n return rows.map(r => this.mapApiKey(r))\n }\n\n // ── Consent Templates ──────────────────────────────────────────────────────\n\n private mapConsentTemplate(r: RawConsentTemplate): ServerConsentTemplate {\n return { id: r.id, tenantId: r.tenant_id, name: r.name, cookies: JSON.parse(r.cookies_json), categories: JSON.parse(r.categories_json), createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createConsentTemplate(data: CreateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const id = randomConsentTemplateId()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO consent_templates (id,tenant_id,name,cookies_json,categories_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?)').run([id, data.tenantId, data.name, JSON.stringify(data.cookies), JSON.stringify(data.categories), now, now])\n return this.mapConsentTemplate(this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as unknown as RawConsentTemplate)\n }\n\n async updateConsentTemplate(id: string, data: UpdateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const now = new Date().toISOString()\n if (data.name !== undefined) this.stmt('UPDATE consent_templates SET name = ?, updated_at = ? WHERE id = ?').run([data.name, now, id])\n if (data.cookies !== undefined) this.stmt('UPDATE consent_templates SET cookies_json = ?, updated_at = ? WHERE id = ?').run([JSON.stringify(data.cookies), now, id])\n if (data.categories !== undefined) this.stmt('UPDATE consent_templates SET categories_json = ?, updated_at = ? WHERE id = ?').run([JSON.stringify(data.categories), now, id])\n const row = this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as RawConsentTemplate | null\n if (!row) throw new Error('Consent template not found')\n return this.mapConsentTemplate(row)\n }\n\n async deleteConsentTemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM consent_templates WHERE id = ?').run(id)\n }\n\n async getConsentTemplate(id: string): Promise<ServerConsentTemplate | null> {\n const row = this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as RawConsentTemplate | null\n return row ? this.mapConsentTemplate(row) : null\n }\n\n async getConsentTemplates(tenantId: string): Promise<ServerConsentTemplate[]> {\n const rows = this.stmt('SELECT * FROM consent_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawConsentTemplate[]\n return rows.map(r => this.mapConsentTemplate(r))\n }\n\n async copyConsentTemplate(id: string, newName: string): Promise<ServerConsentTemplate> {\n const src = await this.getConsentTemplate(id)\n if (!src) throw new Error('Consent template not found')\n return this.createConsentTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies, categories: src.categories })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n private mapUITemplate(r: RawUITemplate): ServerUITemplate {\n const s = JSON.parse(r.settings_json) as Omit<ServerUITemplate, 'id' | 'tenantId' | 'name' | 'createdAt' | 'updatedAt'>\n return { id: r.id, tenantId: r.tenant_id, name: r.name, ...s, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const id = randomUITemplateId()\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n this.stmt('INSERT INTO ui_templates (id,tenant_id,name,settings_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run([id, tenantId, name, JSON.stringify(settings), now, now])\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | null\n if (!row) throw new Error('UI template not found')\n if (data.name !== undefined) this.stmt('UPDATE ui_templates SET name = ?, updated_at = ? WHERE id = ?').run([data.name, now, id])\n const { name: _n, ...settingsUpdate } = data\n if (Object.keys(settingsUpdate).length > 0) {\n const current = JSON.parse(row.settings_json) as Record<string, unknown>\n const merged = { ...current, ...settingsUpdate }\n this.stmt('UPDATE ui_templates SET settings_json = ?, updated_at = ? WHERE id = ?').run([JSON.stringify(merged), now, id])\n }\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM ui_templates WHERE id = ?').run(id)\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | null\n return row ? this.mapUITemplate(row) : null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n const rows = this.stmt('SELECT * FROM ui_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawUITemplate[]\n return rows.map(r => this.mapUITemplate(r))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src\n return this.createUITemplate({ tenantId, name: newName, mainBanner, gpcBanner, preferenceModal })\n }\n\n // ── Profile summaries ──────────────────────────────────────────────────────\n\n private mapProfileSummary(r: RawProfileSummary): ProfileSummary {\n return {\n id: r.id,\n name: r.name,\n defaultLocale: r.default_locale,\n complianceGroup: (r.compliance_group ?? null) as ComplianceGroupId | null,\n customComplianceGroup: r.custom_compliance_group ?? null,\n isActive: r.is_active === 1,\n consentTemplateName: r.consent_template_name,\n uiTemplateName: r.ui_template_name,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n }\n\n private readonly PROFILE_SUMMARY_SQL = `\n SELECT\n p.id, p.name, p.default_locale, p.created_at, p.updated_at,\n json_extract(p.profile_json, '$.complianceGroup') AS compliance_group,\n json_extract(p.profile_json, '$.customComplianceGroup') AS custom_compliance_group,\n json_extract(p.profile_json, '$.isActive') AS is_active,\n json_extract(p.profile_json, '$.consentTemplateId') AS consent_template_id,\n json_extract(p.profile_json, '$.uiTemplateId') AS ui_template_id,\n ct.name AS consent_template_name,\n ut.name AS ui_template_name\n FROM profiles p\n LEFT JOIN consent_templates ct ON ct.id = json_extract(p.profile_json, '$.consentTemplateId')\n LEFT JOIN ui_templates ut ON ut.id = json_extract(p.profile_json, '$.uiTemplateId')\n `\n\n async listProfilesSummary(tenantId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE p.tenant_id = ? ORDER BY p.created_at ASC`\n ).all(tenantId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async findProfilesUsingConsentTemplate(templateId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE json_extract(p.profile_json, '$.consentTemplateId') = ? ORDER BY p.name ASC`\n ).all(templateId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async findProfilesUsingUITemplate(templateId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE json_extract(p.profile_json, '$.uiTemplateId') = ? ORDER BY p.name ASC`\n ).all(templateId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async getOptInStats(tenantId: string, filters: OptInFilters): Promise<OptInStats> {\n const conditions: string[] = ['cr.tenant_id = ?']\n const vals: Val[] = [tenantId]\n if (filters.profileId) { conditions.push('cr.profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('cr.created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('cr.created_at <= ?'); vals.push(filters.to) }\n if (filters.locale) { conditions.push('cr.locale = ?'); vals.push(filters.locale) }\n const where = conditions.join(' AND ')\n\n const rows = this.stmt(\n `SELECT cr.locale, cr.consent_json FROM consent_records cr WHERE ${where}`\n ).all(vals) as unknown as { locale: string; consent_json: string }[]\n\n let total = 0, granted = 0, denied = 0, managed = 0\n const byLocale: Record<string, { total: number; granted: number; denied: number; managed: number }> = {}\n\n for (const r of rows) {\n const statuses = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = statuses.length > 0 ? statuses.filter(s => s === 'granted').length / statuses.length : 0\n const deniedPct = statuses.length > 0 ? statuses.filter(s => s === 'denied').length / statuses.length : 0\n total++\n const loc = r.locale\n if (!byLocale[loc]) byLocale[loc] = { total: 0, granted: 0, denied: 0, managed: 0 }\n byLocale[loc]!.total++\n if (grantedPct >= 0.9) { granted++; byLocale[loc]!.granted++ }\n else if (deniedPct >= 0.9) { denied++; byLocale[loc]!.denied++ }\n else { managed++; byLocale[loc]!.managed++ }\n }\n\n const dateRows = this.stmt(\n `SELECT date(cr.created_at) as d, cr.consent_json FROM consent_records cr WHERE ${where} ORDER BY d ASC`\n ).all(vals) as unknown as { d: string; consent_json: string }[]\n\n const dateMap = new Map<string, { total: number; granted: number; denied: number; managed: number }>()\n for (const r of dateRows) {\n const entry = dateMap.get(r.d) ?? { total: 0, granted: 0, denied: 0, managed: 0 }\n const statuses = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = statuses.length > 0 ? statuses.filter(s => s === 'granted').length / statuses.length : 0\n const deniedPct = statuses.length > 0 ? statuses.filter(s => s === 'denied').length / statuses.length : 0\n entry.total++\n if (grantedPct >= 0.9) entry.granted++\n else if (deniedPct >= 0.9) entry.denied++\n else entry.managed++\n dateMap.set(r.d, entry)\n }\n\n const byDate = Array.from(dateMap.entries()).map(([date, v]) => ({ date, ...v }))\n return {\n total, granted, denied, managed,\n grantedPct: total > 0 ? Math.round((granted / total) * 1000) / 10 : 0,\n deniedPct: total > 0 ? Math.round((denied / total) * 1000) / 10 : 0,\n managedPct: total > 0 ? Math.round((managed / total) * 1000) / 10 : 0,\n byLocale,\n byDate,\n }\n }\n}\n","import type { DatabaseSync, StatementSync } from 'node:sqlite'\n\nconst MAX_PAGE_SIZE = 500\nimport { randomUUID, randomProfileId, randomVisitorId, randomConsentId, randomConsentTemplateId, randomUITemplateId } from '../../utils/crypto'\nimport { resolveStoragePaths } from '../../utils/storage-path'\nimport { SEED_SQL, SCHEMA_SQL_SQLITE } from '../seed-data'\nimport { likePrefix } from '../../utils/sql-search'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n StoredProfileJson,\n ProfileSummary,\n OptInStats,\n OptInFilters,\n ComplianceGroupId,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentSummary,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditLogSummary,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n TenantSettings,\n ServerConsentTemplate,\n ServerUITemplate,\n CreateConsentTemplateInput,\n UpdateConsentTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n NoticeShownRecord,\n CreateNoticeShownInput,\n PagedResult,\n} from '@consenti/types'\n\ninterface RawProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RawConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n signature?: string | null\n}\ninterface RawConsentSummary {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; gpc_detected: number; source: string\n age_verified?: number; created_at: string; updated_at: string\n}\ninterface RawConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RawVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RawNoticeShown {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; created_at: string\n}\ninterface RawUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n allowed_tenants?: string | null\n}\n\ninterface RawProfileSummary {\n id: string; name: string; default_locale: string\n compliance_group: string | null; custom_compliance_group: string | null; is_active: number\n consent_template_name: string | null; ui_template_name: string | null\n created_at: string; updated_at: string\n consent_template_id: string | null; ui_template_id: string | null\n}\ninterface RawRole {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RawPermission {\n id: string; name: string; description: string | null\n}\ninterface RawAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RawAuditLogSummary {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null; created_at: string\n}\ninterface RawCount { total: number }\ninterface RawTenant {\n id: string; name: string; slug: string; created_at: string; updated_at: string\n}\ninterface RawApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number\n created_by: string | null; expire_by: string | null; created_at: string; updated_at: string | null\n}\ninterface RawConsentTemplate {\n id: string; tenant_id: string; name: string; cookies_json: string; categories_json: string; created_at: string; updated_at: string\n}\ninterface RawUITemplate {\n id: string; tenant_id: string; name: string; settings_json: string; created_at: string; updated_at: string\n}\n\n\ntype Stmt = StatementSync\n\nexport class NodeSqliteBuiltinAdapter implements StorageAdapter {\n private db!: DatabaseSync\n private cache = new Map<string, Stmt>()\n\n constructor(private config: StorageConfig) {}\n\n private stmt(sql: string): Stmt {\n let s = this.cache.get(sql)\n if (!s) { s = this.db.prepare(sql); this.cache.set(sql, s) }\n return s\n }\n\n async connect(): Promise<void> {\n // Lazy: node:sqlite is only available on Node.js >= 22.5.0.\n // Loading it here (not at module load) prevents startup failure on Node 20.\n const { DatabaseSync: Db } = await import('node:sqlite').catch(() => {\n throw new Error(`driver 'node:sqlite' requires Node.js >= 22.5.0 (current: ${process.version})`)\n }) as { DatabaseSync: new(path: string) => DatabaseSync }\n const { dbPath } = resolveStoragePaths(this.config.path ?? './consenti-data', 'node:sqlite')\n this.db = new Db(dbPath)\n this.db.exec('PRAGMA journal_mode=WAL')\n this.db.exec('PRAGMA foreign_keys=ON')\n this.db.exec('PRAGMA synchronous=NORMAL')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n this.cache.clear()\n this.db.close()\n }\n\n async migrate(): Promise<void> {\n // No installations predate this schema, so a fresh connect always creates the full current\n // table set in one pass — no incremental/versioned steps needed. `PRAGMA user_version` is\n // kept as the hook for whenever a *real* future migration is needed: add `if (version < N)`\n // blocks below the initial creation, the same shape as the removed historical ones.\n const row = this.db.prepare('PRAGMA user_version').get() as { user_version: number }\n if (row.user_version < 1) {\n this.db.exec(SCHEMA_SQL_SQLITE)\n this.db.exec(SEED_SQL)\n this.db.exec('PRAGMA user_version = 1')\n }\n\n // Plain-column indexes (tenant_id, created_at, visitor_id, etc.) are generated into\n // SCHEMA_SQL_SQLITE from seed-data.ts's declarative `idx` field — see the `if` block above.\n // Only the JSON-expression index below survives here: SQLite's json_extract() functional\n // index has no equivalent representation across the Postgres/MySQL/Mongo dialects, so it\n // can't live in the shared declarative system.\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_profiles_tenant_compliance_active\n ON profiles (tenant_id, json_extract(profile_json,'$.complianceGroup'), json_extract(profile_json,'$.isActive'));\n `)\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n private mapProfile(row: RawProfile): Profile {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n defaultLocale: row.default_locale,\n version: row.version,\n profileJson: JSON.parse(row.profile_json) as StoredProfileJson,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO profiles (id,tenant_id,name,default_locale,version,profile_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.defaultLocale, 1, JSON.stringify(data.profileJson), now, now)\n const profile = await this.getProfile(id)\n if (!profile) throw new Error('Failed to create profile')\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const existing = await this.getProfile(id)\n if (!existing) throw new Error(`Profile ${id} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.defaultLocale != null) { fields.push('default_locale = ?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { fields.push('profile_json = ?'); vals.push(JSON.stringify(data.profileJson)) }\n if (data.version != null) { fields.push('version = ?'); vals.push(data.version) }\n vals.push(id)\n this.stmt(`UPDATE profiles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const updated = await this.getProfile(id)\n if (!updated) throw new Error('Failed to update profile')\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.stmt('DELETE FROM profiles WHERE id = ?').run(id)\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const row = this.stmt('SELECT * FROM profiles WHERE id = ?').get(id) as RawProfile | undefined\n return row ? this.mapProfile(row) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = this.stmt('SELECT * FROM profiles WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawProfile[]\n return rows.map(r => this.mapProfile(r))\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n const row = this.stmt(\n `SELECT * FROM profiles WHERE tenant_id = ?\n AND (json_extract(profile_json, '$.complianceGroup') = ? OR json_extract(profile_json, '$.customComplianceGroup') = ?)\n AND json_extract(profile_json, '$.isActive') = 1 LIMIT 1`\n ).get(tenantId, complianceGroup, complianceGroup) as RawProfile | undefined\n return row ? this.mapProfile(row) : null\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n private mapConsent(row: RawConsent): ConsentDbRecord {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n profileId: row.profile_id,\n locale: row.locale,\n consentJson: JSON.parse(row.consent_json) as ConsentValue,\n gpcDetected: row.gpc_detected === 1,\n source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n ...(row.parental_consent_token != null ? { parentalConsentToken: row.parental_consent_token } : {}),\n ...(row.tcf_string != null ? { tcfString: row.tcf_string } : {}),\n ...(row.signature != null ? { signature: row.signature } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n private static readonly CONSENT_SUMMARY_COLS =\n 'id, tenant_id, visitor_id, profile_id, locale, gpc_detected, source, age_verified, created_at, updated_at'\n\n private mapConsentSummary(row: RawConsentSummary): ConsentSummary {\n return {\n id: row.id, tenantId: row.tenant_id, visitorId: row.visitor_id, profileId: row.profile_id,\n locale: row.locale, gpcDetected: row.gpc_detected === 1, source: row.source as ConsentDbRecord['source'],\n ...(row.age_verified != null && row.age_verified !== 0 ? { ageVerified: true } : {}),\n createdAt: row.created_at, updatedAt: row.updated_at,\n }\n }\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomConsentId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO consent_records (id,tenant_id,visitor_id,profile_id,locale,consent_json,gpc_detected,source,age_verified,parental_consent_token,tcf_string,signature,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.profileId, data.locale, JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source, data.ageVerified ? 1 : 0, data.parentalConsentToken ?? null, data.tcfString ?? null, data.signature ?? null, now, now)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, data.tenantId, id, data.visitorId, null, JSON.stringify(data.consentJson), 'created', now)\n const record = await this.getConsent(data.visitorId)\n if (!record) throw new Error('Failed to create consent')\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?', 'consent_json = ?']\n const vals: (string | number | null)[] = [now, JSON.stringify(data.consentJson)]\n if (data.locale != null) { fields.push('locale = ?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { fields.push('gpc_detected = ?'); vals.push(data.gpcDetected ? 1 : 0) }\n if (data.signature != null) { fields.push('signature = ?'); vals.push(data.signature) }\n vals.push(visitorId)\n this.stmt(`UPDATE consent_records SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const histId = randomUUID()\n this.stmt(\n 'INSERT INTO consent_history (id,tenant_id,consent_record_id,visitor_id,old_json,new_json,action,created_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(histId, existing.tenantId, existing.id, visitorId, JSON.stringify(existing.consentJson), JSON.stringify(data.consentJson), 'updated', now)\n const updated = await this.getConsent(visitorId)\n if (!updated) throw new Error('Failed to update consent')\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitorId)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitorId)\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const row = this.stmt('SELECT * FROM consent_records WHERE visitor_id = ?').get(visitorId) as RawConsent | undefined\n return row ? this.mapConsent(row) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.profileId) { conditions.push('profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(visitor_id LIKE ? OR profile_id LIKE ? OR locale LIKE ? OR source LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like, like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT ${NodeSqliteBuiltinAdapter.CONSENT_SUMMARY_COLS} FROM consent_records WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, limit, offset) as unknown as RawConsentSummary[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM consent_records WHERE ${where}`).get(...vals) as unknown as RawCount\n return { items: rows.map(r => this.mapConsentSummary(r)), total: countRow.total, page, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n 'SELECT * FROM consent_records WHERE tenant_id = ? LIMIT ? OFFSET ?'\n ).all(filters.tenantId, pageSize, offset) as unknown as RawConsent[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapConsent(row)\n offset += pageSize\n }\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = this.stmt(\n 'SELECT * FROM consent_history WHERE visitor_id = ? ORDER BY created_at ASC'\n ).all(visitorId) as unknown as RawConsentHistory[]\n return rows.map(r => ({\n id: r.id,\n tenantId: r.tenant_id,\n consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json != null ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'],\n createdAt: r.created_at,\n }))\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO notice_shown (id,tenant_id,visitor_id,profile_id,locale,created_at) VALUES (?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.profileId, data.locale, now)\n return { id, tenantId: data.tenantId, visitorId: data.visitorId, profileId: data.profileId, locale: data.locale, createdAt: now }\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n const rows = this.stmt(\n 'SELECT * FROM notice_shown WHERE visitor_id = ? ORDER BY created_at DESC'\n ).all(visitorId) as unknown as RawNoticeShown[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n profileId: r.profile_id, locale: r.locale, createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n private mapVisitor(row: RawVisitor): Visitor {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n visitorId: row.visitor_id,\n firstSeen: row.first_seen,\n lastSeen: row.last_seen,\n ...(row.country != null ? { country: row.country } : {}),\n ...(row.region != null ? { region: row.region } : {}),\n ...(row.city != null ? { city: row.city } : {}),\n ...(row.ip_hash != null ? { ipHash: row.ip_hash } : {}),\n ...(row.user_agent_hash != null ? { userAgentHash: row.user_agent_hash } : {}),\n }\n }\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomVisitorId()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO visitors (id,tenant_id,visitor_id,country,region,city,ip_hash,user_agent_hash,first_seen,last_seen) VALUES (?,?,?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.visitorId, data.country ?? null, data.region ?? null, data.city ?? null, data.ipHash ?? null, data.userAgentHash ?? null, now, now)\n const visitor = await this.getVisitor(data.visitorId)\n if (!visitor) throw new Error('Failed to create visitor')\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const fields: string[] = ['last_seen = ?']\n const vals: (string | number | null)[] = [data.lastSeen ?? now]\n if (data.country != null) { fields.push('country = ?'); vals.push(data.country) }\n if (data.region != null) { fields.push('region = ?'); vals.push(data.region) }\n if (data.city != null) { fields.push('city = ?'); vals.push(data.city) }\n vals.push(visitorId)\n this.stmt(`UPDATE visitors SET ${fields.join(', ')} WHERE visitor_id = ?`).run(...vals)\n const visitor = await this.getVisitor(visitorId)\n if (!visitor) throw new Error(`Visitor ${visitorId} not found`)\n return visitor\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.stmt('DELETE FROM visitors WHERE visitor_id = ?').run(visitorId)\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const row = this.stmt('SELECT * FROM visitors WHERE visitor_id = ?').get(visitorId) as RawVisitor | undefined\n return row ? this.mapVisitor(row) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.from) { conditions.push('first_seen >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('first_seen <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(visitor_id LIKE ? OR country LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT * FROM visitors WHERE ${where} ORDER BY last_seen DESC LIMIT ? OFFSET ?`\n ).all(...vals, limit, offset) as unknown as RawVisitor[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM visitors WHERE ${where}`).get(...vals) as unknown as RawCount\n return { items: rows.map(r => this.mapVisitor(r)), total: countRow.total, page, limit }\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────\n\n private mapUser(row: RawUser): AdminUser {\n const allowedTenants: string[] = row.allowed_tenants\n ? JSON.parse(row.allowed_tenants) as string[]\n : []\n return {\n id: row.id,\n tenantId: row.tenant_id,\n name: row.name,\n email: row.email,\n passwordHash: row.password_hash,\n isActive: row.is_active === 1,\n totpEnabled: row.totp_enabled === 1,\n ...(row.totp_secret != null ? { totpSecret: row.totp_secret } : {}),\n allowedTenants,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n }\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const allowedTenantsJson = data.allowedTenants?.length ? JSON.stringify(data.allowedTenants) : null\n this.stmt(\n 'INSERT INTO users (id,tenant_id,name,email,password_hash,allowed_tenants,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)'\n ).run(id, data.tenantId, data.name, data.email, data.passwordHash, allowedTenantsJson, now, now)\n const user = await this.getUserByEmail(data.email)\n if (!user) throw new Error('Failed to create user')\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number | null)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.email != null) { fields.push('email = ?'); vals.push(data.email) }\n if (data.passwordHash != null) { fields.push('password_hash = ?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { fields.push('is_active = ?'); vals.push(data.isActive ? 1 : 0) }\n if ('totpSecret' in data) { fields.push('totp_secret = ?'); vals.push(data.totpSecret ?? null) }\n if (data.totpEnabled != null) { fields.push('totp_enabled = ?'); vals.push(data.totpEnabled ? 1 : 0) }\n if ('allowedTenants' in data) {\n fields.push('allowed_tenants = ?')\n vals.push(data.allowedTenants?.length ? JSON.stringify(data.allowedTenants) : null)\n }\n vals.push(id)\n this.stmt(`UPDATE users SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n if (!row) throw new Error(`User ${id} not found`)\n return this.mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n this.stmt('DELETE FROM user_roles WHERE user_id = ?').run(id)\n this.stmt('DELETE FROM users WHERE id = ?').run(id)\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE id = ?').get(id) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const row = this.stmt('SELECT * FROM users WHERE email = ?').get(email) as RawUser | undefined\n return row ? this.mapUser(row) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = this.stmt('SELECT * FROM users WHERE tenant_id = ? ORDER BY created_at ASC').all(tenantId) as unknown as RawUser[]\n return rows.map(r => this.mapUser(r))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const row = tenantId\n ? this.stmt('SELECT COUNT(*) as n FROM users WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n : this.stmt('SELECT COUNT(*) as n FROM users').get() as { n: number } | undefined\n return row?.n ?? 0\n }\n\n // ── Roles & Permissions ───────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = this.stmt('SELECT * FROM roles WHERE tenant_id = ?').all(tenantId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n this.stmt('INSERT INTO roles (id,tenant_id,name,description) VALUES (?,?,?,?)').run(\n id, data.tenantId, data.name, data.description ?? null\n )\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error('Failed to create role')\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const fields: string[] = []\n const vals: (string | number | null)[] = []\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.description != null) { fields.push('description = ?'); vals.push(data.description) }\n if (fields.length > 0) {\n vals.push(id)\n this.stmt(`UPDATE roles SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n }\n const row = this.stmt('SELECT * FROM roles WHERE id = ?').get(id) as RawRole | undefined\n if (!row) throw new Error(`Role ${id} not found`)\n return { id: row.id, tenantId: row.tenant_id, name: row.name, ...(row.description != null ? { description: row.description } : {}) }\n }\n\n async deleteRole(id: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM user_roles WHERE role_id = ?').run(id)\n this.stmt('DELETE FROM roles WHERE id = ?').run(id)\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = this.stmt('SELECT * FROM permissions ORDER BY name ASC').all() as unknown as RawPermission[]\n return rows.map(r => ({ id: r.id, name: r.name, ...(r.description != null ? { description: r.description } : {}) }))\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO role_permissions (role_id,permission_id) VALUES (?,?)').run(roleId, permissionId)\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.stmt('DELETE FROM role_permissions WHERE role_id = ? AND permission_id = ?').run(roleId, permissionId)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = this.stmt(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON rp.permission_id = p.id WHERE rp.role_id = ?'\n ).all(roleId) as unknown as RawPermission[]\n return rows.map(r => ({\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ? AND ur.tenant_id = ?'\n ).all(userId, tenantId) as unknown as RawRole[]\n : this.stmt(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON ur.role_id = r.id WHERE ur.user_id = ?'\n ).all(userId) as unknown as RawRole[]\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n this.stmt('INSERT OR IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)').run(userId, roleId, tenantId)\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ? AND tenant_id = ?').run(userId, roleId, tenantId)\n } else {\n this.stmt('DELETE FROM user_roles WHERE user_id = ? AND role_id = ?').run(userId, roleId)\n }\n }\n\n // ── Audit ─────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt(\n 'INSERT INTO audit_logs (id,tenant_id,user_id,action,resource_type,resource_id,old_data,new_data,created_at) VALUES (?,?,?,?,?,?,?,?,?)'\n ).run(\n id, data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null,\n now\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.resourceType) { conditions.push('resource_type = ?'); vals.push(filters.resourceType) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n if (filters.q) {\n conditions.push(\"(action LIKE ? OR resource_type LIKE ? OR resource_id LIKE ? OR user_id LIKE ?) ESCAPE '\\\\'\")\n const like = likePrefix(filters.q)\n vals.push(like, like, like, like)\n }\n const where = conditions.join(' AND ')\n const page = filters.page ?? 1\n const limit = Math.min(filters.limit ?? 50, MAX_PAGE_SIZE)\n const offset = (page - 1) * limit\n const rows = this.stmt(\n `SELECT ${NodeSqliteBuiltinAdapter.AUDIT_SUMMARY_COLS} FROM audit_logs WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, limit, offset) as unknown as RawAuditLogSummary[]\n const countRow = this.stmt(`SELECT COUNT(*) AS total FROM audit_logs WHERE ${where}`).get(...vals) as unknown as RawCount\n return { items: rows.map(r => this.mapAuditSummary(r)), total: countRow.total, page, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n const row = this.stmt('SELECT * FROM audit_logs WHERE id = ?').get(id) as RawAuditLog | undefined\n return row ? this.mapAuditLog(row) : null\n }\n\n private static readonly AUDIT_SUMMARY_COLS = 'id, tenant_id, user_id, action, resource_type, resource_id, created_at'\n\n private mapAuditSummary(r: RawAuditLogSummary): AuditLogSummary {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n createdAt: r.created_at,\n }\n }\n\n private mapAuditLog(r: RawAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action,\n resourceType: r.resource_type, createdAt: r.created_at,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n }\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const conditions: string[] = ['tenant_id = ?']\n const vals: (string | number | null)[] = [filters.tenantId]\n if (filters.action) { conditions.push('action = ?'); vals.push(filters.action) }\n if (filters.from) { conditions.push('created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('created_at <= ?'); vals.push(filters.to) }\n const pageSize = 500\n let offset = 0\n while (true) {\n const rows = this.stmt(\n `SELECT * FROM audit_logs WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ? OFFSET ?`\n ).all(...vals, pageSize, offset) as unknown as RawAuditLog[]\n if (rows.length === 0) break\n for (const row of rows) yield this.mapAuditLog(row)\n offset += pageSize\n }\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const tc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const gc = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const vc = this.stmt('SELECT COUNT(*) as n FROM visitors WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const totalConsents = tc?.n ?? 0\n\n const recent = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 500'\n ).all(tenantId) as unknown as { consent_json: string }[]\n\n let accepted = 0, rejected = 0\n for (const r of recent) {\n const vals = Object.values(JSON.parse(r.consent_json) as Record<string, string>)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n\n return {\n totalConsents,\n gpcDetectedCount: gc?.n ?? 0,\n totalVisitors: vc?.n ?? 0,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = this.stmt(\n 'SELECT consent_json FROM consent_records WHERE tenant_id = ? LIMIT 5000'\n ).all(tenantId) as unknown as { consent_json: string }[]\n const stats: CategoryStats = {}\n for (const row of rows) {\n const consent = JSON.parse(row.consent_json) as Record<string, string>\n for (const [key, status] of Object.entries(consent)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const rows = this.stmt(\n `SELECT date(created_at) as date, COUNT(*) as count FROM consent_records WHERE tenant_id = ? AND created_at >= date('now', ?) GROUP BY date(created_at) ORDER BY date ASC`\n ).all(tenantId, `-${days} days`) as unknown as { date: string; count: number }[]\n return rows.map(r => ({ date: r.date, count: r.count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = this.stmt(\n `SELECT v.country, COUNT(*) as count FROM consent_records cr JOIN visitors v ON v.visitor_id = cr.visitor_id WHERE cr.tenant_id = ? AND v.country IS NOT NULL GROUP BY v.country ORDER BY count DESC LIMIT 50`\n ).all(tenantId) as unknown as { country: string; count: number }[]\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ?').get(tenantId) as { n: number } | undefined\n const detected = this.stmt('SELECT COUNT(*) as n FROM consent_records WHERE tenant_id = ? AND gpc_detected = 1').get(tenantId) as { n: number } | undefined\n const t = total?.n ?? 0\n const d = detected?.n ?? 0\n return { detected: d, total: t, rate: t > 0 ? Math.round((d / t) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = this.stmt('SELECT * FROM tenants ORDER BY name ASC').all() as unknown as RawTenant[]\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO tenants (id,name,slug,created_at,updated_at) VALUES (?,?,?,?,?)').run(id, data.name, data.slug, now, now)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error('Failed to create tenant')\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const fields: string[] = ['updated_at = ?']\n const vals: (string | number)[] = [now]\n if (data.name != null) { fields.push('name = ?'); vals.push(data.name) }\n if (data.slug != null) { fields.push('slug = ?'); vals.push(data.slug) }\n vals.push(id)\n this.stmt(`UPDATE tenants SET ${fields.join(', ')} WHERE id = ?`).run(...vals)\n const row = this.stmt('SELECT * FROM tenants WHERE id = ?').get(id) as RawTenant | undefined\n if (!row) throw new Error(`Tenant ${id} not found`)\n return { id: row.id, name: row.name, slug: row.slug, createdAt: row.created_at, updatedAt: row.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.stmt('DELETE FROM tenants WHERE id = ?').run(id)\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n const row = this.stmt('SELECT allowed_origins_json, admin_allowed_origins_json, setup_completed FROM tenant_settings WHERE tenant_id = ?')\n .get(tenantId) as { allowed_origins_json: string; admin_allowed_origins_json: string; setup_completed: number } | undefined\n return row\n ? {\n allowedOrigins: JSON.parse(row.allowed_origins_json) as string[],\n adminAllowedOrigins: JSON.parse(row.admin_allowed_origins_json) as string[],\n setupCompleted: !!row.setup_completed,\n }\n : {}\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const current = await this.getSettings(tenantId)\n const merged = { ...current, ...data }\n this.stmt(\n `INSERT INTO tenant_settings (tenant_id, allowed_origins_json, admin_allowed_origins_json, setup_completed, updated_at) VALUES (?,?,?,?,?)\n ON CONFLICT(tenant_id) DO UPDATE SET allowed_origins_json = excluded.allowed_origins_json, admin_allowed_origins_json = excluded.admin_allowed_origins_json, setup_completed = excluded.setup_completed, updated_at = excluded.updated_at`,\n ).run(tenantId, JSON.stringify(merged.allowedOrigins ?? []), JSON.stringify(merged.adminAllowedOrigins ?? []), merged.setupCompleted ? 1 : 0, new Date().toISOString())\n return this.getSettings(tenantId)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = this.stmt(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?'\n ).all(cutoff) as unknown as { visitor_id: string }[]\n for (const { visitor_id } of toDelete) {\n this.stmt('DELETE FROM consent_history WHERE visitor_id = ?').run(visitor_id)\n this.stmt('DELETE FROM consent_records WHERE visitor_id = ?').run(visitor_id)\n }\n return toDelete.length\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const { count } = this.stmt('SELECT COUNT(*) AS count FROM audit_logs WHERE created_at < ?').get(cutoff) as { count: number }\n this.stmt('DELETE FROM audit_logs WHERE created_at < ?').run(cutoff)\n return count\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RawApiKey): ApiKey {\n return {\n id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1,\n ...(r.created_by !== null ? { createdBy: r.created_by } : {}),\n ...(r.expire_by !== null ? { expireBy: r.expire_by } : {}),\n createdAt: r.created_at,\n ...(r.updated_at !== null ? { updatedAt: r.updated_at } : {}),\n }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO api_keys (id,tenant_id,key_hash,name,created_by,expire_by,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)')\n .run(id, data.tenantId, data.keyHash, data.name, data.createdBy ?? null, data.expireBy ?? null, now, now)\n const row = this.stmt('SELECT * FROM api_keys WHERE id = ?').get(id) as RawApiKey | undefined\n if (!row) throw new Error('Failed to create API key')\n return this.mapApiKey(row)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const row = this.stmt('SELECT * FROM api_keys WHERE key_hash = ? AND is_active = 1').get(keyHash) as RawApiKey | undefined\n return row ? this.mapApiKey(row) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n this.stmt('UPDATE api_keys SET is_active = 0, updated_at = ? WHERE id = ?').run(new Date().toISOString(), id)\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n const now = new Date().toISOString()\n this.stmt(\n 'UPDATE api_keys SET is_active = 1, updated_at = ?, expire_by = CASE WHEN expire_by <= ? THEN NULL ELSE expire_by END WHERE id = ?',\n ).run(now, now, id)\n }\n\n async deleteApiKey(id: string): Promise<void> {\n this.stmt('DELETE FROM api_keys WHERE id = ?').run(id)\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = this.stmt('SELECT * FROM api_keys WHERE tenant_id = ? ORDER BY created_at DESC').all(tenantId) as unknown as RawApiKey[]\n return rows.map(r => this.mapApiKey(r))\n }\n\n // ── Consent Templates ──────────────────────────────────────────────────────\n\n private mapConsentTemplate(r: RawConsentTemplate): ServerConsentTemplate {\n return { id: r.id, tenantId: r.tenant_id, name: r.name, cookies: JSON.parse(r.cookies_json), categories: JSON.parse(r.categories_json), createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createConsentTemplate(data: CreateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const id = randomConsentTemplateId()\n const now = new Date().toISOString()\n this.stmt('INSERT INTO consent_templates (id,tenant_id,name,cookies_json,categories_json,created_at,updated_at) VALUES (?,?,?,?,?,?,?)').run(id, data.tenantId, data.name, JSON.stringify(data.cookies), JSON.stringify(data.categories), now, now)\n return this.mapConsentTemplate(this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as unknown as RawConsentTemplate)\n }\n\n async updateConsentTemplate(id: string, data: UpdateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const now = new Date().toISOString()\n if (data.name !== undefined) this.stmt('UPDATE consent_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n if (data.cookies !== undefined) this.stmt('UPDATE consent_templates SET cookies_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(data.cookies), now, id)\n if (data.categories !== undefined) this.stmt('UPDATE consent_templates SET categories_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(data.categories), now, id)\n const row = this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as RawConsentTemplate | undefined\n if (!row) throw new Error('Consent template not found')\n return this.mapConsentTemplate(row)\n }\n\n async deleteConsentTemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM consent_templates WHERE id = ?').run(id)\n }\n\n async getConsentTemplate(id: string): Promise<ServerConsentTemplate | null> {\n const row = this.stmt('SELECT * FROM consent_templates WHERE id = ?').get(id) as RawConsentTemplate | undefined\n return row ? this.mapConsentTemplate(row) : null\n }\n\n async getConsentTemplates(tenantId: string): Promise<ServerConsentTemplate[]> {\n const rows = this.stmt('SELECT * FROM consent_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawConsentTemplate[]\n return rows.map(r => this.mapConsentTemplate(r))\n }\n\n async copyConsentTemplate(id: string, newName: string): Promise<ServerConsentTemplate> {\n const src = await this.getConsentTemplate(id)\n if (!src) throw new Error('Consent template not found')\n return this.createConsentTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies, categories: src.categories })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n private mapUITemplate(r: RawUITemplate): ServerUITemplate {\n const s = JSON.parse(r.settings_json) as Omit<ServerUITemplate, 'id' | 'tenantId' | 'name' | 'createdAt' | 'updatedAt'>\n return { id: r.id, tenantId: r.tenant_id, name: r.name, ...s, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const id = randomUITemplateId()\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n this.stmt('INSERT INTO ui_templates (id,tenant_id,name,settings_json,created_at,updated_at) VALUES (?,?,?,?,?,?)').run(id, tenantId, name, JSON.stringify(settings), now, now)\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n if (!row) throw new Error('UI template not found')\n if (data.name !== undefined) this.stmt('UPDATE ui_templates SET name = ?, updated_at = ? WHERE id = ?').run(data.name, now, id)\n const { name: _n, ...settingsUpdate } = data\n if (Object.keys(settingsUpdate).length > 0) {\n const current = JSON.parse(row.settings_json) as Record<string, unknown>\n const merged = { ...current, ...settingsUpdate }\n this.stmt('UPDATE ui_templates SET settings_json = ?, updated_at = ? WHERE id = ?').run(JSON.stringify(merged), now, id)\n }\n return this.mapUITemplate(this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as unknown as RawUITemplate)\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.stmt('DELETE FROM ui_templates WHERE id = ?').run(id)\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n const row = this.stmt('SELECT * FROM ui_templates WHERE id = ?').get(id) as RawUITemplate | undefined\n return row ? this.mapUITemplate(row) : null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n const rows = this.stmt('SELECT * FROM ui_templates WHERE tenant_id = ? ORDER BY name ASC').all(tenantId) as unknown as RawUITemplate[]\n return rows.map(r => this.mapUITemplate(r))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src\n return this.createUITemplate({ tenantId, name: newName, mainBanner, gpcBanner, preferenceModal })\n }\n\n // ── Profile summaries ──────────────────────────────────────────────────────\n\n private mapProfileSummary(r: RawProfileSummary): ProfileSummary {\n return {\n id: r.id,\n name: r.name,\n defaultLocale: r.default_locale,\n complianceGroup: (r.compliance_group ?? null) as ComplianceGroupId | null,\n customComplianceGroup: r.custom_compliance_group ?? null,\n isActive: r.is_active === 1,\n consentTemplateName: r.consent_template_name,\n uiTemplateName: r.ui_template_name,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n }\n\n private readonly PROFILE_SUMMARY_SQL = `\n SELECT\n p.id, p.name, p.default_locale, p.created_at, p.updated_at,\n json_extract(p.profile_json, '$.complianceGroup') AS compliance_group,\n json_extract(p.profile_json, '$.customComplianceGroup') AS custom_compliance_group,\n json_extract(p.profile_json, '$.isActive') AS is_active,\n json_extract(p.profile_json, '$.consentTemplateId') AS consent_template_id,\n json_extract(p.profile_json, '$.uiTemplateId') AS ui_template_id,\n ct.name AS consent_template_name,\n ut.name AS ui_template_name\n FROM profiles p\n LEFT JOIN consent_templates ct ON ct.id = json_extract(p.profile_json, '$.consentTemplateId')\n LEFT JOIN ui_templates ut ON ut.id = json_extract(p.profile_json, '$.uiTemplateId')\n `\n\n async listProfilesSummary(tenantId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE p.tenant_id = ? ORDER BY p.created_at ASC`\n ).all(tenantId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async findProfilesUsingConsentTemplate(templateId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE json_extract(p.profile_json, '$.consentTemplateId') = ? ORDER BY p.name ASC`\n ).all(templateId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n async findProfilesUsingUITemplate(templateId: string): Promise<ProfileSummary[]> {\n const rows = this.stmt(\n `${this.PROFILE_SUMMARY_SQL} WHERE json_extract(p.profile_json, '$.uiTemplateId') = ? ORDER BY p.name ASC`\n ).all(templateId) as unknown as RawProfileSummary[]\n return rows.map(r => this.mapProfileSummary(r))\n }\n\n // ── Analytics ──────────────────────────────────────────────────────────────\n\n async getOptInStats(tenantId: string, filters: OptInFilters): Promise<OptInStats> {\n const conditions: string[] = ['cr.tenant_id = ?']\n const vals: (string | number | null)[] = [tenantId]\n if (filters.profileId) { conditions.push('cr.profile_id = ?'); vals.push(filters.profileId) }\n if (filters.from) { conditions.push('cr.created_at >= ?'); vals.push(filters.from) }\n if (filters.to) { conditions.push('cr.created_at <= ?'); vals.push(filters.to) }\n if (filters.locale) { conditions.push('cr.locale = ?'); vals.push(filters.locale) }\n\n const where = conditions.join(' AND ')\n\n const rows = this.stmt(\n `SELECT cr.locale, cr.consent_json FROM consent_records cr WHERE ${where}`\n ).all(...vals) as unknown as { locale: string; consent_json: string }[]\n\n let total = 0, granted = 0, denied = 0, managed = 0\n const byLocale: Record<string, { total: number; granted: number; denied: number; managed: number }> = {}\n\n for (const r of rows) {\n const consent = JSON.parse(r.consent_json) as Record<string, string>\n const statuses = Object.values(consent)\n const grantedCount = statuses.filter(s => s === 'granted').length\n const deniedCount = statuses.filter(s => s === 'denied').length\n const grantedPct = statuses.length > 0 ? grantedCount / statuses.length : 0\n\n total++\n const loc = r.locale\n if (!byLocale[loc]) byLocale[loc] = { total: 0, granted: 0, denied: 0, managed: 0 }\n byLocale[loc]!.total++\n\n if (grantedPct >= 0.9) { granted++; byLocale[loc]!.granted++ }\n else if (deniedCount / (statuses.length || 1) >= 0.9) { denied++; byLocale[loc]!.denied++ }\n else { managed++; byLocale[loc]!.managed++ }\n }\n\n // byDate — aggregate by date(created_at)\n const dateRows = this.stmt(\n `SELECT date(cr.created_at) as d, cr.consent_json FROM consent_records cr WHERE ${where} ORDER BY d ASC`\n ).all(...vals) as unknown as { d: string; consent_json: string }[]\n\n const dateMap = new Map<string, { total: number; granted: number; denied: number; managed: number }>()\n for (const r of dateRows) {\n const entry = dateMap.get(r.d) ?? { total: 0, granted: 0, denied: 0, managed: 0 }\n const consent = JSON.parse(r.consent_json) as Record<string, string>\n const statuses = Object.values(consent)\n const grantedPct = statuses.length > 0 ? statuses.filter(s => s === 'granted').length / statuses.length : 0\n const deniedPct = statuses.length > 0 ? statuses.filter(s => s === 'denied').length / statuses.length : 0\n entry.total++\n if (grantedPct >= 0.9) entry.granted++\n else if (deniedPct >= 0.9) entry.denied++\n else entry.managed++\n dateMap.set(r.d, entry)\n }\n\n const byDate = Array.from(dateMap.entries()).map(([date, v]) => ({ date, ...v }))\n\n return {\n total, granted, denied, managed,\n grantedPct: total > 0 ? Math.round((granted / total) * 1000) / 10 : 0,\n deniedPct: total > 0 ? Math.round((denied / total) * 1000) / 10 : 0,\n managedPct: total > 0 ? Math.round((managed / total) * 1000) / 10 : 0,\n byLocale,\n byDate,\n }\n }\n}\n","import type { MongoClient, Db } from 'mongodb'\nimport { randomUUID, randomProfileId, randomVisitorId, randomConsentId } from '../../utils/crypto'\nimport { SEED_TENANT, SEED_PERMISSIONS, SEED_ROLES, SEED_ROLE_PERMISSIONS, ALL_INDEXES } from '../seed-data'\nimport type {\n StorageAdapter, StorageConfig, Profile, StoredProfileJson,\n CreateProfileInput, UpdateProfileInput,\n ConsentDbRecord, ConsentSummary, ConsentHistoryEntry, ConsentValue, ConsentFilters,\n CreateConsentInput, UpdateConsentInput,\n Visitor, VisitorFilters, CreateVisitorInput, UpdateVisitorInput,\n AdminUser, CreateUserInput, UpdateUserInput,\n Role, CreateRoleInput, UpdateRoleInput,\n Permission, AuditLog, AuditLogSummary, AuditFilters, CreateAuditLogInput,\n OverviewStats, CategoryStats, TimelineEntry,\n CountryStat, GpcStats, Tenant, ApiKey, CreateApiKeyInput,\n CreateTenantInput, UpdateTenantInput, TenantSettings,\n NoticeShownRecord, CreateNoticeShownInput, PagedResult,\n} from '@consenti/types'\n\n// ── Raw document shapes ────────────────────────────────────────────────────────\n\ninterface DocProfile {\n _id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: StoredProfileJson\n created_at: string; updated_at: string\n}\ninterface DocConsent {\n _id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; consent_json: ConsentValue\n gpc_detected: boolean; source: string; created_at: string; updated_at: string\n age_verified?: boolean; parental_consent_token?: string; tcf_string?: string\n signature?: string\n}\ninterface DocConsentSummary {\n _id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; gpc_detected: boolean; source: string\n created_at: string; updated_at: string\n age_verified?: boolean\n}\ninterface DocConsentHistory {\n _id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: ConsentValue | null; new_json: ConsentValue; action: string; created_at: string\n}\ninterface DocVisitor {\n _id: string; tenant_id: string; visitor_id: string\n country?: string; region?: string; city?: string\n ip_hash?: string; user_agent_hash?: string\n first_seen: string; last_seen: string\n}\ninterface DocNoticeShown {\n _id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; created_at: string\n}\ninterface DocUser {\n _id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: boolean; created_at: string; updated_at: string\n}\ninterface DocRole { _id: string; tenant_id: string; name: string; description?: string }\ninterface DocPermission { _id: string; name: string; description?: string }\ninterface DocUserRole { user_id: string; role_id: string; tenant_id?: string }\ninterface DocRolePermission { role_id: string; permission_id: string }\ninterface DocAuditLog {\n _id: string; tenant_id: string; user_id?: string; action: string\n resource_type: string; resource_id?: string\n old_data?: unknown; new_data?: unknown; created_at: string\n}\ninterface DocAuditLogSummary {\n _id: string; tenant_id: string; user_id?: string; action: string\n resource_type: string; resource_id?: string; created_at: string\n}\ninterface DocApiKey {\n _id: string; tenant_id: string; key_hash: string; name: string; is_active: boolean\n created_by?: string; expire_by?: string; created_at: string; updated_at?: string\n}\n\n// ── Mapper helpers ─────────────────────────────────────────────────────────────\n\nfunction cast<T extends object>(doc: object | null): T | null {\n return doc as T | null\n}\n\nfunction castArr<T extends object>(docs: object[]): T[] {\n return docs as T[]\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction mapProfile(d: DocProfile): Profile {\n return {\n id: d._id, tenantId: d.tenant_id, name: d.name,\n defaultLocale: d.default_locale, version: d.version,\n profileJson: d.profile_json, createdAt: d.created_at, updatedAt: d.updated_at,\n }\n}\n\nfunction mapConsent(d: DocConsent): ConsentDbRecord {\n return {\n id: d._id, tenantId: d.tenant_id, visitorId: d.visitor_id, profileId: d.profile_id,\n locale: d.locale, consentJson: d.consent_json,\n gpcDetected: d.gpc_detected, source: d.source as ConsentDbRecord['source'],\n createdAt: d.created_at, updatedAt: d.updated_at,\n ...(d.age_verified != null ? { ageVerified: d.age_verified } : {}),\n ...(d.parental_consent_token != null ? { parentalConsentToken: d.parental_consent_token } : {}),\n ...(d.tcf_string != null ? { tcfString: d.tcf_string } : {}),\n ...(d.signature != null ? { signature: d.signature } : {}),\n }\n}\n\nconst CONSENT_SUMMARY_PROJECTION = { tenant_id: 1, visitor_id: 1, profile_id: 1, locale: 1, gpc_detected: 1, source: 1, age_verified: 1, created_at: 1, updated_at: 1 }\n\nfunction mapConsentSummary(d: DocConsentSummary): ConsentSummary {\n return {\n id: d._id, tenantId: d.tenant_id, visitorId: d.visitor_id, profileId: d.profile_id,\n locale: d.locale, gpcDetected: d.gpc_detected, source: d.source as ConsentDbRecord['source'],\n createdAt: d.created_at, updatedAt: d.updated_at,\n ...(d.age_verified != null ? { ageVerified: d.age_verified } : {}),\n }\n}\n\nfunction mapHistory(d: DocConsentHistory): ConsentHistoryEntry {\n return {\n id: d._id, tenantId: d.tenant_id, consentRecordId: d.consent_record_id,\n visitorId: d.visitor_id, oldJson: d.old_json, newJson: d.new_json,\n action: d.action as ConsentHistoryEntry['action'], createdAt: d.created_at,\n }\n}\n\nfunction mapVisitor(d: DocVisitor): Visitor {\n return {\n id: d._id, tenantId: d.tenant_id, visitorId: d.visitor_id,\n ...(d.country != null ? { country: d.country } : {}),\n ...(d.region != null ? { region: d.region } : {}),\n ...(d.city != null ? { city: d.city } : {}),\n ...(d.ip_hash != null ? { ipHash: d.ip_hash } : {}),\n ...(d.user_agent_hash != null ? { userAgentHash: d.user_agent_hash } : {}),\n firstSeen: d.first_seen, lastSeen: d.last_seen,\n }\n}\n\nfunction mapUser(d: DocUser): AdminUser {\n return {\n id: d._id, tenantId: d.tenant_id, name: d.name, email: d.email,\n passwordHash: d.password_hash, isActive: d.is_active,\n createdAt: d.created_at, updatedAt: d.updated_at,\n }\n}\n\nfunction mapRole(d: DocRole): Role {\n return {\n id: d._id, tenantId: d.tenant_id, name: d.name,\n ...(d.description != null ? { description: d.description } : {}),\n }\n}\n\nfunction mapPermission(d: DocPermission): Permission {\n return {\n id: d._id, name: d.name,\n ...(d.description != null ? { description: d.description } : {}),\n }\n}\n\nfunction mapAuditLog(d: DocAuditLog): AuditLog {\n return {\n id: d._id, tenantId: d.tenant_id, action: d.action, resourceType: d.resource_type,\n ...(d.user_id != null ? { userId: d.user_id } : {}),\n ...(d.resource_id != null ? { resourceId: d.resource_id } : {}),\n ...(d.old_data != null ? { oldData: d.old_data } : {}),\n ...(d.new_data != null ? { newData: d.new_data } : {}),\n createdAt: d.created_at,\n }\n}\n\nconst AUDIT_SUMMARY_PROJECTION = { tenant_id: 1, user_id: 1, action: 1, resource_type: 1, resource_id: 1, created_at: 1 }\n\nfunction mapAuditSummary(d: DocAuditLogSummary): AuditLogSummary {\n return {\n id: d._id, tenantId: d.tenant_id, action: d.action, resourceType: d.resource_type,\n ...(d.user_id != null ? { userId: d.user_id } : {}),\n ...(d.resource_id != null ? { resourceId: d.resource_id } : {}),\n createdAt: d.created_at,\n }\n}\n\n// ── Adapter ────────────────────────────────────────────────────────────────────\n\n// Custom thin wrappers so MongoDB's ObjectId generics don't interfere with our string _id.\n// DocCursor extends AsyncIterable so it supports `for await...of` iteration.\ninterface DocCursor extends AsyncIterable<object> {\n sort(s: object): DocCursor\n skip(n: number): DocCursor\n limit(n: number): DocCursor\n toArray(): Promise<object[]>\n}\ninterface DocCollection {\n insertOne(doc: object): Promise<object>\n findOne(filter: object): Promise<object | null>\n findOneAndUpdate(filter: object, update: object, options?: object): Promise<object | null>\n find(filter: object, options?: { projection: object }): DocCursor\n updateOne(filter: object, update: object, options?: object): Promise<object>\n deleteOne(filter: object): Promise<object>\n deleteMany(filter: object): Promise<object>\n countDocuments(filter?: object): Promise<number>\n createIndex(spec: object, options?: object): Promise<object>\n aggregate(pipeline: object[]): { toArray(): Promise<object[]> }\n}\n\nexport class MongoDBAdapter implements StorageAdapter {\n private client!: MongoClient\n private db!: Db\n\n constructor(private config: StorageConfig) { }\n\n private col(name: string): DocCollection {\n // MongoDB's Collection generics assume ObjectId for _id; we use string UUIDs throughout.\n // The cast via unknown is the TypeScript-sanctioned way to bridge structurally incompatible types.\n return this.db.collection(name) as unknown as DocCollection\n }\n\n async connect(): Promise<void> {\n const { MongoClient: MC } = (await import('mongodb')) as { MongoClient: typeof MongoClient }\n const uri = this.config.uri ?? 'mongodb://localhost:27017'\n this.client = new MC(uri)\n await this.client.connect()\n this.db = this.client.db(this.config.database ?? 'consenti')\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.client.close()\n }\n\n async migrate(): Promise<void> {\n await this.createIndexes()\n await this.seed()\n }\n\n private async createIndexes(): Promise<void> {\n // Single source of truth: apps/api/src/storage/seed-data.ts's `ALL_INDEXES`, generated from\n // the same TableDef list that drives CREATE INDEX for SQLite/Postgres/MySQL. createIndex() is\n // idempotent (a no-op when an identical index already exists), and this only runs once per\n // process at connect() time — not per request.\n for (const { table, cols, unique } of ALL_INDEXES) {\n const spec = Object.fromEntries(cols.map(c => [c, 1]))\n await this.col(table).createIndex(spec, unique ? { unique: true } : undefined)\n }\n }\n\n private async seed(): Promise<void> {\n const { id, name, slug } = SEED_TENANT\n if (!(await this.col('tenants').findOne({ _id: id }))) {\n await this.col('tenants').insertOne({ _id: id, name, slug })\n }\n for (const p of SEED_PERMISSIONS) {\n const doc = { _id: p.id, name: p.name, description: p.description }\n await this.col('permissions').updateOne({ _id: p.id }, { $setOnInsert: doc }, { upsert: true })\n }\n for (const r of SEED_ROLES) {\n const doc = { _id: r.id, tenant_id: r.tenantId, name: r.name, description: r.description }\n await this.col('roles').updateOne({ _id: r.id }, { $setOnInsert: doc }, { upsert: true })\n }\n const rp = this.col('role_permissions')\n for (const { roleId, permissionId } of SEED_ROLE_PERMISSIONS) {\n await rp.updateOne(\n { role_id: roleId, permission_id: permissionId },\n { $setOnInsert: { role_id: roleId, permission_id: permissionId } },\n { upsert: true },\n )\n }\n }\n\n // ── Profiles ─────────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const now = new Date().toISOString()\n const id = randomProfileId()\n const doc: DocProfile = {\n _id: id, tenant_id: data.tenantId, name: data.name,\n default_locale: data.defaultLocale, version: 1,\n profile_json: data.profileJson,\n created_at: now, updated_at: now,\n }\n await this.col('profiles').insertOne(doc)\n return mapProfile(doc)\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const now = new Date().toISOString()\n const set: Record<string, unknown> = { updated_at: now }\n if (data.name != null) set['name'] = data.name\n if (data.defaultLocale != null) set['default_locale'] = data.defaultLocale\n if (data.profileJson != null) set['profile_json'] = data.profileJson\n if (data.version != null) set['version'] = data.version\n const res = cast<DocProfile>(await this.col('profiles').findOneAndUpdate(\n { _id: id }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Profile ${id} not found`)\n return mapProfile(res)\n }\n\n async deleteProfile(id: string): Promise<void> {\n await this.col('profiles').deleteOne({ _id: id })\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const doc = cast<DocProfile>(await this.col('profiles').findOne({ _id: id }))\n return doc ? mapProfile(doc) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const docs = castArr<DocProfile>(await this.col('profiles').find({ tenant_id: tenantId }).toArray())\n return docs.map(mapProfile)\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n const profiles = await this.getProfiles(tenantId)\n return profiles.find(p =>\n (p.profileJson.complianceGroup === complianceGroup || (p.profileJson as { customComplianceGroup?: string }).customComplianceGroup === complianceGroup) &&\n p.profileJson.isActive\n ) ?? null\n }\n\n // ── Consents ─────────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const now = new Date().toISOString()\n const doc: DocConsent = {\n _id: randomConsentId(), tenant_id: data.tenantId, visitor_id: data.visitorId,\n profile_id: data.profileId,\n locale: data.locale, consent_json: data.consentJson,\n gpc_detected: data.gpcDetected, source: data.source,\n created_at: now, updated_at: now,\n ...(data.ageVerified != null ? { age_verified: data.ageVerified } : {}),\n ...(data.parentalConsentToken != null ? { parental_consent_token: data.parentalConsentToken } : {}),\n ...(data.tcfString != null ? { tcf_string: data.tcfString } : {}),\n ...(data.signature != null ? { signature: data.signature } : {}),\n }\n await this.col('consent_records').insertOne(doc)\n await this.writeHistory(doc._id, data.tenantId, data.visitorId, null, data.consentJson, 'created')\n return mapConsent(doc)\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const now = new Date().toISOString()\n const set: Record<string, unknown> = { updated_at: now, consent_json: data.consentJson }\n if (data.locale != null) set['locale'] = data.locale\n if (data.gpcDetected != null) set['gpc_detected'] = data.gpcDetected\n if (data.signature != null) set['signature'] = data.signature\n const res = cast<DocConsent>(await this.col('consent_records').findOneAndUpdate(\n { visitor_id: visitorId }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Consent for visitor ${visitorId} not found`)\n await this.writeHistory(res._id, res.tenant_id, visitorId, existing.consentJson, data.consentJson, 'updated')\n return mapConsent(res)\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n await this.col('consent_history').deleteMany({ visitor_id: visitorId })\n await this.col('consent_records').deleteOne({ visitor_id: visitorId })\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const doc = cast<DocConsent>(await this.col('consent_records').findOne({ visitor_id: visitorId }))\n return doc ? mapConsent(doc) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n const q = this.buildConsentFilter(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [docsRaw, total] = await Promise.all([\n this.col('consent_records').find(q, { projection: CONSENT_SUMMARY_PROJECTION })\n .sort({ created_at: -1 }).skip((page - 1) * limit).limit(limit).toArray(),\n this.col('consent_records').countDocuments(q),\n ])\n return { items: castArr<DocConsentSummary>(docsRaw).map(mapConsentSummary), total, page, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const q = this.buildConsentFilter(filters)\n const cursor = this.col('consent_records').find(q).sort({ created_at: 1 })\n for await (const doc of cursor) yield mapConsent(doc as unknown as DocConsent)\n }\n\n private buildConsentFilter(f: ConsentFilters): Record<string, unknown> {\n const q: Record<string, unknown> = { tenant_id: f.tenantId }\n if (f.profileId != null) q['profile_id'] = f.profileId\n if (f.from != null || f.to != null) {\n const range: Record<string, string> = {}\n if (f.from != null) range['$gte'] = f.from\n if (f.to != null) range['$lte'] = f.to\n q['created_at'] = range\n }\n if (f.q) {\n // Anchored prefix match (not a mid-string wildcard) — the only regex shape that can use a\n // b-tree index on any dialect, matching how visitor/profile IDs and codes are actually\n // searched (pasted or typed from the start, never a remembered middle fragment).\n const re = { $regex: '^' + escapeRegex(f.q), $options: 'i' }\n q['$or'] = [{ visitor_id: re }, { profile_id: re }, { locale: re }, { source: re }]\n }\n return q\n }\n\n private async writeHistory(\n consentRecordId: string, tenantId: string, visitorId: string,\n oldJson: ConsentValue | null, newJson: ConsentValue, action: string,\n ): Promise<void> {\n const doc: DocConsentHistory = {\n _id: randomUUID(), tenant_id: tenantId, consent_record_id: consentRecordId,\n visitor_id: visitorId, old_json: oldJson, new_json: newJson,\n action, created_at: new Date().toISOString(),\n }\n await this.col('consent_history').insertOne(doc)\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const docs = castArr<DocConsentHistory>(\n await this.col('consent_history').find({ visitor_id: visitorId }).sort({ created_at: 1 }).toArray(),\n )\n return docs.map(mapHistory)\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const doc: DocNoticeShown = {\n _id: randomUUID(), tenant_id: data.tenantId, visitor_id: data.visitorId,\n profile_id: data.profileId, locale: data.locale, created_at: new Date().toISOString(),\n }\n await this.col('notice_shown').insertOne(doc)\n return {\n id: doc._id, tenantId: doc.tenant_id, visitorId: doc.visitor_id,\n profileId: doc.profile_id, locale: doc.locale, createdAt: doc.created_at,\n }\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n const docs = castArr<DocNoticeShown>(\n await this.col('notice_shown').find({ visitor_id: visitorId }).sort({ created_at: -1 }).toArray(),\n )\n return docs.map(d => ({\n id: d._id, tenantId: d.tenant_id, visitorId: d.visitor_id,\n profileId: d.profile_id, locale: d.locale, createdAt: d.created_at,\n }))\n }\n\n // ── Visitors ─────────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const doc: DocVisitor = {\n _id: randomVisitorId(), tenant_id: data.tenantId, visitor_id: data.visitorId,\n first_seen: now, last_seen: now,\n ...(data.country != null ? { country: data.country } : {}),\n ...(data.region != null ? { region: data.region } : {}),\n ...(data.city != null ? { city: data.city } : {}),\n ...(data.ipHash != null ? { ip_hash: data.ipHash } : {}),\n ...(data.userAgentHash != null ? { user_agent_hash: data.userAgentHash } : {}),\n }\n await this.col('visitors').insertOne(doc)\n return mapVisitor(doc)\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const set: Record<string, string> = { last_seen: data.lastSeen ?? new Date().toISOString() }\n if (data.country != null) set['country'] = data.country\n if (data.region != null) set['region'] = data.region\n if (data.city != null) set['city'] = data.city\n const res = cast<DocVisitor>(await this.col('visitors').findOneAndUpdate(\n { visitor_id: visitorId }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Visitor ${visitorId} not found`)\n return mapVisitor(res)\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n await this.col('visitors').deleteOne({ visitor_id: visitorId })\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const doc = cast<DocVisitor>(await this.col('visitors').findOne({ visitor_id: visitorId }))\n return doc ? mapVisitor(doc) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n const q: Record<string, unknown> = { tenant_id: filters.tenantId }\n if (filters.from != null || filters.to != null) {\n const r: Record<string, string> = {}\n if (filters.from != null) r['$gte'] = filters.from\n if (filters.to != null) r['$lte'] = filters.to\n q['first_seen'] = r\n }\n if (filters.q) {\n const re = { $regex: '^' + escapeRegex(filters.q), $options: 'i' }\n q['$or'] = [{ visitor_id: re }, { country: re }]\n }\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [docsRaw, total] = await Promise.all([\n this.col('visitors').find(q).sort({ first_seen: -1 }).skip((page - 1) * limit).limit(limit).toArray(),\n this.col('visitors').countDocuments(q),\n ])\n return { items: castArr<DocVisitor>(docsRaw).map(mapVisitor), total, page, limit }\n }\n\n // ── Stats ─────────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const [total, gpcCount, visitors] = await Promise.all([\n this.col('consent_records').countDocuments({ tenant_id: tenantId }),\n this.col('consent_records').countDocuments({ tenant_id: tenantId, gpc_detected: true }),\n this.col('visitors').countDocuments({ tenant_id: tenantId }),\n ])\n\n const pipeline = [\n { $match: { tenant_id: tenantId } },\n { $addFields: { values: { $objectToArray: '$consent_json' } } },\n { $unwind: '$values' },\n { $group: { _id: '$values.v', count: { $sum: 1 } } },\n ]\n const results = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: string; count: number }>\n\n let granted = 0, denied = 0, all = 0\n for (const r of results) {\n all += r.count\n if (r._id === 'granted') granted = r.count\n if (r._id === 'denied') denied = r.count\n }\n return {\n totalConsents: total,\n acceptedPct: all > 0 ? Math.round((granted / all) * 100) : 0,\n rejectedPct: all > 0 ? Math.round((denied / all) * 100) : 0,\n totalVisitors: visitors,\n gpcDetectedCount: gpcCount,\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const pipeline = [\n { $match: { tenant_id: tenantId } },\n { $addFields: { pairs: { $objectToArray: '$consent_json' } } },\n { $unwind: '$pairs' },\n { $group: { _id: { cookie: '$pairs.k', status: '$pairs.v' }, count: { $sum: 1 } } },\n ]\n const rows = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: { cookie: string; status: string }; count: number }>\n const result: CategoryStats = {}\n for (const r of rows) {\n const cookie = r._id.cookie\n if (!result[cookie]) result[cookie] = { granted: 0, denied: 0, objected: 0 }\n const entry = result[cookie]\n if (entry) {\n if (r._id.status === 'granted') entry.granted += r.count\n if (r._id.status === 'denied') entry.denied += r.count\n if (r._id.status === 'objected') entry.objected += r.count\n }\n }\n return result\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const from = new Date(Date.now() - days * 86400_000).toISOString()\n const pipeline = [\n { $match: { tenant_id: tenantId, created_at: { $gte: from } } },\n { $group: { _id: { $substr: ['$created_at', 0, 10] }, count: { $sum: 1 } } },\n { $sort: { _id: 1 } },\n ]\n const rows = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: string; count: number }>\n return rows.map(r => ({ date: r._id, count: r.count }))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const doc: DocUser = {\n _id: randomUUID(), tenant_id: data.tenantId, name: data.name,\n email: data.email, password_hash: data.passwordHash,\n is_active: true, created_at: now, updated_at: now,\n }\n await this.col('users').insertOne(doc)\n return mapUser(doc)\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const set: Record<string, unknown> = { updated_at: new Date().toISOString() }\n if (data.name != null) set['name'] = data.name\n if (data.email != null) set['email'] = data.email\n if (data.passwordHash != null) set['password_hash'] = data.passwordHash\n if (data.isActive != null) set['is_active'] = data.isActive\n const res = cast<DocUser>(await this.col('users').findOneAndUpdate(\n { _id: id }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`User ${id} not found`)\n return mapUser(res)\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.col('user_roles').deleteMany({ user_id: id })\n await this.col('users').deleteOne({ _id: id })\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const doc = cast<DocUser>(await this.col('users').findOne({ _id: id }))\n return doc ? mapUser(doc) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const doc = cast<DocUser>(await this.col('users').findOne({ email }))\n return doc ? mapUser(doc) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const docs = castArr<DocUser>(await this.col('users').find({ tenant_id: tenantId }).toArray())\n return docs.map(mapUser)\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n return this.col('users').countDocuments(tenantId ? { tenant_id: tenantId } : {})\n }\n\n // ── Roles & permissions ───────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const docs = castArr<DocRole>(await this.col('roles').find({ tenant_id: tenantId }).toArray())\n return docs.map(mapRole)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const doc: DocRole = {\n _id: randomUUID(), tenant_id: data.tenantId, name: data.name,\n ...(data.description != null ? { description: data.description } : {}),\n }\n await this.col('roles').insertOne(doc)\n return mapRole(doc)\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const set: Record<string, string> = {}\n if (data.name != null) set['name'] = data.name\n if (data.description != null) set['description'] = data.description\n const res = cast<DocRole>(await this.col('roles').findOneAndUpdate(\n { _id: id }, { $set: set }, { returnDocument: 'after' },\n ))\n if (!res) throw new Error(`Role ${id} not found`)\n return mapRole(res)\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.col('role_permissions').deleteMany({ role_id: id })\n await this.col('user_roles').deleteMany({ role_id: id })\n await this.col('roles').deleteOne({ _id: id })\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const docs = castArr<DocPermission>(await this.col('permissions').find({}).toArray())\n return docs.map(mapPermission)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rps = castArr<DocRolePermission>(\n await this.col('role_permissions').find({ role_id: roleId }).toArray(),\n )\n const ids = rps.map(r => r.permission_id)\n if (ids.length === 0) return []\n const docs = castArr<DocPermission>(await this.col('permissions').find({ _id: { $in: ids } }).toArray())\n return docs.map(mapPermission)\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const filter = tenantId\n ? { user_id: userId, tenant_id: tenantId }\n : { user_id: userId }\n const urs = castArr<DocUserRole>(await this.col('user_roles').find(filter).toArray())\n const ids = urs.map(r => r.role_id)\n if (ids.length === 0) return []\n const docs = castArr<DocRole>(await this.col('roles').find({ _id: { $in: ids } }).toArray())\n return docs.map(mapRole)\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n await this.col('user_roles').updateOne(\n { user_id: userId, role_id: roleId, tenant_id: tenantId },\n { $setOnInsert: { user_id: userId, role_id: roleId, tenant_id: tenantId } },\n { upsert: true },\n )\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n await this.col('user_roles').deleteOne({ user_id: userId, role_id: roleId, tenant_id: tenantId })\n } else {\n await this.col('user_roles').deleteMany({ user_id: userId, role_id: roleId })\n }\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n await this.col('role_permissions').updateOne(\n { role_id: roleId, permission_id: permissionId },\n { $setOnInsert: { role_id: roleId, permission_id: permissionId } },\n { upsert: true },\n )\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n await this.col('role_permissions').deleteOne({ role_id: roleId, permission_id: permissionId })\n }\n\n // ── Audit logs ────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const doc: DocAuditLog = {\n _id: randomUUID(), tenant_id: data.tenantId, action: data.action,\n resource_type: data.resourceType,\n ...(data.userId != null ? { user_id: data.userId } : {}),\n ...(data.resourceId != null ? { resource_id: data.resourceId } : {}),\n ...(data.oldData != null ? { old_data: data.oldData } : {}),\n ...(data.newData != null ? { new_data: data.newData } : {}),\n created_at: new Date().toISOString(),\n }\n await this.col('audit_logs').insertOne(doc)\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n const q = this.buildAuditFilter(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [docsRaw, total] = await Promise.all([\n this.col('audit_logs').find(q, { projection: AUDIT_SUMMARY_PROJECTION })\n .sort({ created_at: -1 }).skip((page - 1) * limit).limit(limit).toArray(),\n this.col('audit_logs').countDocuments(q),\n ])\n return { items: castArr<DocAuditLogSummary>(docsRaw).map(mapAuditSummary), total, page, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n const doc = cast<DocAuditLog>(await this.col('audit_logs').findOne({ _id: id }))\n return doc ? mapAuditLog(doc) : null\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const q = this.buildAuditFilter(filters)\n const cursor = this.col('audit_logs').find(q).sort({ created_at: 1 })\n for await (const doc of cursor) yield mapAuditLog(doc as unknown as DocAuditLog)\n }\n\n private buildAuditFilter(f: AuditFilters): Record<string, unknown> {\n const q: Record<string, unknown> = { tenant_id: f.tenantId }\n if (f.action != null) q['action'] = f.action\n if (f.resourceType != null) q['resource_type'] = f.resourceType\n if (f.from != null || f.to != null) {\n const r: Record<string, string> = {}\n if (f.from != null) r['$gte'] = f.from\n if (f.to != null) r['$lte'] = f.to\n q['created_at'] = r\n }\n if (f.q) {\n const re = { $regex: '^' + escapeRegex(f.q), $options: 'i' }\n q['$or'] = [{ action: re }, { resource_type: re }, { resource_id: re }, { user_id: re }]\n }\n return q\n }\n\n // ── Stats extensions ──────────────────────────────────────────────────────────\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const pipeline = [\n { $match: { tenant_id: tenantId } },\n { $lookup: { from: 'visitors', localField: 'visitor_id', foreignField: 'visitor_id', as: 'v' } },\n { $unwind: '$v' },\n { $match: { 'v.country': { $ne: null } } },\n { $group: { _id: '$v.country', count: { $sum: 1 } } },\n { $sort: { count: -1 } },\n { $limit: 50 },\n ]\n const rows = await this.col('consent_records').aggregate(pipeline).toArray() as Array<{ _id: string; count: number }>\n return rows.map(r => ({ country: r._id, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const total = await this.col('consent_records').countDocuments({ tenant_id: tenantId })\n const detected = await this.col('consent_records').countDocuments({ tenant_id: tenantId, gpc_detected: true })\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ───────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const docs = castArr<{ _id: string; name: string; slug: string; created_at: string; updated_at: string }>(\n await this.col('tenants').find({}).toArray()\n )\n return docs.map(d => ({ id: d._id, name: d.name, slug: d.slug, createdAt: d.created_at, updatedAt: d.updated_at }))\n }\n\n // ── API Keys ──────────────────────────────────────────────────────────────────\n\n private mapApiKey(d: DocApiKey): ApiKey {\n return {\n id: d._id, tenantId: d.tenant_id, keyHash: d.key_hash, name: d.name, isActive: d.is_active,\n ...(d.created_by !== undefined ? { createdBy: d.created_by } : {}),\n ...(d.expire_by !== undefined ? { expireBy: d.expire_by } : {}),\n createdAt: d.created_at,\n ...(d.updated_at !== undefined ? { updatedAt: d.updated_at } : {}),\n }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const now = new Date().toISOString()\n const doc: DocApiKey = {\n _id: randomUUID(), tenant_id: data.tenantId, key_hash: data.keyHash,\n name: data.name, is_active: true,\n ...(data.createdBy !== undefined ? { created_by: data.createdBy } : {}),\n ...(data.expireBy !== undefined ? { expire_by: data.expireBy } : {}),\n created_at: now, updated_at: now,\n }\n await this.col('api_keys').insertOne(doc)\n return this.mapApiKey(doc)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const doc = cast<DocApiKey>(await this.col('api_keys').findOne({ key_hash: keyHash, is_active: true }))\n return doc ? this.mapApiKey(doc) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n await this.col('api_keys').updateOne({ _id: id }, { $set: { is_active: false, updated_at: new Date().toISOString() } })\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n const doc = cast<DocApiKey>(await this.col('api_keys').findOne({ _id: id }))\n if (!doc) return\n const expired = doc.expire_by && new Date(doc.expire_by) <= new Date()\n await this.col('api_keys').updateOne(\n { _id: id },\n {\n $set: { is_active: true, updated_at: new Date().toISOString() },\n ...(expired ? { $unset: { expire_by: '' } } : {}),\n },\n )\n }\n\n async deleteApiKey(id: string): Promise<void> {\n await this.col('api_keys').deleteOne({ _id: id })\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const docs = castArr<DocApiKey>(await this.col('api_keys').find({ tenant_id: tenantId }).sort({ created_at: -1 }).toArray())\n return docs.map(d => this.mapApiKey(d))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const doc = { _id: id, name: data.name, slug: data.slug, created_at: now, updated_at: now }\n await this.col('tenants').insertOne(doc)\n return { id, name: data.name, slug: data.slug, createdAt: now, updatedAt: now }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const update: Record<string, string> = { updated_at: now }\n if (data.name != null) update['name'] = data.name\n if (data.slug != null) update['slug'] = data.slug\n await this.col('tenants').updateOne({ _id: id }, { $set: update })\n const doc = cast<{ _id: string; name: string; slug: string; created_at: string; updated_at: string }>(\n await this.col('tenants').findOne({ _id: id })\n )\n if (!doc) throw new Error(`Tenant ${id} not found`)\n return { id: doc._id, name: doc.name, slug: doc.slug, createdAt: doc.created_at, updatedAt: doc.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n await this.col('tenants').deleteOne({ _id: id })\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n const doc = cast<{ _id: string; allowed_origins?: string[]; admin_allowed_origins?: string[]; setup_completed?: boolean }>(\n await this.col('tenant_settings').findOne({ _id: tenantId })\n )\n return {\n ...(doc?.allowed_origins !== undefined ? { allowedOrigins: doc.allowed_origins } : {}),\n ...(doc?.admin_allowed_origins !== undefined ? { adminAllowedOrigins: doc.admin_allowed_origins } : {}),\n ...(doc?.setup_completed !== undefined ? { setupCompleted: doc.setup_completed } : {}),\n }\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const update: Record<string, unknown> = { updated_at: new Date().toISOString() }\n if (data.allowedOrigins !== undefined) update['allowed_origins'] = data.allowedOrigins\n if (data.adminAllowedOrigins !== undefined) update['admin_allowed_origins'] = data.adminAllowedOrigins\n if (data.setupCompleted !== undefined) update['setup_completed'] = data.setupCompleted\n await this.col('tenant_settings').updateOne({ _id: tenantId }, { $set: update }, { upsert: true })\n return this.getSettings(tenantId)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const docs = castArr<{ visitor_id: string }>(\n await this.col('consent_records').find({ created_at: { $lt: cutoff } }).toArray()\n )\n if (docs.length === 0) return 0\n const visitorIds = docs.map(d => d.visitor_id)\n await this.col('consent_history').deleteMany({ visitor_id: { $in: visitorIds } })\n await this.col('consent_records').deleteMany({ visitor_id: { $in: visitorIds } })\n return docs.length\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const filter = { created_at: { $lt: cutoff } }\n const count = await this.col('audit_logs').countDocuments(filter)\n await this.col('audit_logs').deleteMany(filter)\n return count\n }\n\n // Template methods — not yet implemented for MongoDB adapter\n async createConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteConsentTemplate(): Promise<void> { throw new Error('Not implemented') }\n async getConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async getConsentTemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async createUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteUITemplate(): Promise<void> { throw new Error('Not implemented') }\n async getUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async getUITemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyUITemplate(): Promise<never> { throw new Error('Not implemented') }\n\n // Profile summary / analytics — not yet implemented for MongoDB adapter\n async listProfilesSummary(): Promise<never[]> { return [] }\n async findProfilesUsingConsentTemplate(): Promise<never[]> { return [] }\n async findProfilesUsingUITemplate(): Promise<never[]> { return [] }\n async getOptInStats(): Promise<never> { throw new Error('Not implemented') }\n}\n","import type { Pool, PoolConnection, RowDataPacket, ResultSetHeader } from 'mysql2/promise'\nimport { randomUUID, randomProfileId, randomVisitorId, randomConsentId } from '../../utils/crypto'\nimport { SEED_TENANT, SEED_PERMISSIONS, SEED_ROLES, SEED_ROLE_PERMISSIONS, SCHEMA_SQL_MYSQL } from '../seed-data'\nimport { likePrefix } from '../../utils/sql-search'\nimport type {\n StorageAdapter, StorageConfig, Profile, StoredProfileJson,\n CreateProfileInput, UpdateProfileInput,\n ConsentDbRecord, ConsentSummary, ConsentHistoryEntry, ConsentValue, ConsentFilters,\n CreateConsentInput, UpdateConsentInput,\n Visitor, VisitorFilters, CreateVisitorInput, UpdateVisitorInput,\n AdminUser, CreateUserInput, UpdateUserInput,\n Role, CreateRoleInput, UpdateRoleInput,\n Permission, AuditLog, AuditLogSummary, AuditFilters, CreateAuditLogInput,\n OverviewStats, CategoryStats, TimelineEntry,\n CountryStat, GpcStats, Tenant, ApiKey, CreateApiKeyInput,\n CreateTenantInput, UpdateTenantInput, TenantSettings,\n NoticeShownRecord, CreateNoticeShownInput, PagedResult,\n} from '@consenti/types'\n\n// ── Raw row shapes ─────────────────────────────────────────────────────────────\n\ninterface RowProfile extends RowDataPacket {\n id: string; tenant_id: string; name: string; default_locale: string\n version: number; profile_json: string\n created_at: string; updated_at: string\n}\ninterface RowConsent extends RowDataPacket {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; consent_json: string\n gpc_detected: number; source: string; created_at: string; updated_at: string\n age_verified?: number; parental_consent_token?: string | null; tcf_string?: string | null\n signature?: string | null\n}\ninterface RowConsentSummary extends RowDataPacket {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; gpc_detected: number; source: string\n age_verified?: number; created_at: string; updated_at: string\n}\ninterface RowConsentHistory extends RowDataPacket {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: string | null; new_json: string; action: string; created_at: string\n}\ninterface RowVisitor extends RowDataPacket {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RowNoticeShown extends RowDataPacket {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; created_at: string\n}\ninterface RowUser extends RowDataPacket {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: number; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: number\n}\ninterface RowRole extends RowDataPacket {\n id: string; tenant_id: string; name: string; description: string | null\n}\ninterface RowPermission extends RowDataPacket {\n id: string; name: string; description: string | null\n}\ninterface RowAuditLog extends RowDataPacket {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: string | null; new_data: string | null; created_at: string\n}\ninterface RowAuditLogSummary extends RowDataPacket {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null; created_at: string\n}\ninterface RowCount extends RowDataPacket { total: number }\ninterface RowStatus extends RowDataPacket { status: string; count: number }\ninterface RowDateCount extends RowDataPacket { date: string; count: number }\ninterface RowCookieStatus extends RowDataPacket { cookie_id: string; status: string; count: number }\ninterface RowCountry extends RowDataPacket { country: string; count: number }\ninterface RowApiKey extends RowDataPacket {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: number\n created_by: string | null; expire_by: string | null; created_at: string; updated_at: string | null\n}\ninterface RowTenant extends RowDataPacket { id: string; name: string; slug: string; created_at: string; updated_at: string }\ninterface RowTenantSettings extends RowDataPacket { allowed_origins_json: string; admin_allowed_origins_json: string; setup_completed: number }\n\n// ── Mappers ────────────────────────────────────────────────────────────────────\n\nfunction mapProfile(r: RowProfile): Profile {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n defaultLocale: r.default_locale, version: r.version,\n profileJson: JSON.parse(r.profile_json) as StoredProfileJson,\n createdAt: r.created_at, updatedAt: r.updated_at,\n }\n}\n\nfunction mapConsent(r: RowConsent): ConsentDbRecord {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id,\n locale: r.locale,\n consentJson: JSON.parse(r.consent_json) as ConsentValue,\n gpcDetected: r.gpc_detected === 1, source: r.source as ConsentDbRecord['source'],\n createdAt: r.created_at, updatedAt: r.updated_at,\n ...(r.age_verified != null ? { ageVerified: r.age_verified === 1 } : {}),\n ...(r.parental_consent_token != null ? { parentalConsentToken: r.parental_consent_token } : {}),\n ...(r.tcf_string != null ? { tcfString: r.tcf_string } : {}),\n ...(r.signature != null ? { signature: r.signature } : {}),\n }\n}\n\nconst CONSENT_SUMMARY_COLS = 'id, tenant_id, visitor_id, profile_id, locale, gpc_detected, source, age_verified, created_at, updated_at'\n\nfunction mapConsentSummary(r: RowConsentSummary): ConsentSummary {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id,\n locale: r.locale, gpcDetected: r.gpc_detected === 1, source: r.source as ConsentDbRecord['source'],\n createdAt: r.created_at, updatedAt: r.updated_at,\n ...(r.age_verified != null ? { ageVerified: r.age_verified === 1 } : {}),\n }\n}\n\nfunction mapHistory(r: RowConsentHistory): ConsentHistoryEntry {\n return {\n id: r.id, tenantId: r.tenant_id, consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json ? JSON.parse(r.old_json) as ConsentValue : null,\n newJson: JSON.parse(r.new_json) as ConsentValue,\n action: r.action as ConsentHistoryEntry['action'], createdAt: r.created_at,\n }\n}\n\nfunction mapVisitor(r: RowVisitor): Visitor {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n ...(r.country != null ? { country: r.country } : {}),\n ...(r.region != null ? { region: r.region } : {}),\n ...(r.city != null ? { city: r.city } : {}),\n ...(r.ip_hash != null ? { ipHash: r.ip_hash } : {}),\n ...(r.user_agent_hash != null ? { userAgentHash: r.user_agent_hash } : {}),\n firstSeen: r.first_seen, lastSeen: r.last_seen,\n }\n}\n\nfunction mapUser(r: RowUser): AdminUser {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name, email: r.email,\n passwordHash: r.password_hash, isActive: r.is_active === 1,\n createdAt: r.created_at, updatedAt: r.updated_at,\n totpEnabled: r.totp_enabled === 1,\n ...(r.totp_secret != null ? { totpSecret: r.totp_secret } : {}),\n }\n}\n\nfunction mapRole(r: RowRole): Role {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapPermission(r: RowPermission): Permission {\n return {\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapAuditLog(r: RowAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: JSON.parse(r.old_data) as unknown } : {}),\n ...(r.new_data != null ? { newData: JSON.parse(r.new_data) as unknown } : {}),\n createdAt: r.created_at,\n }\n}\n\nconst AUDIT_SUMMARY_COLS = 'id, tenant_id, user_id, action, resource_type, resource_id, created_at'\n\nfunction mapAuditSummary(r: RowAuditLogSummary): AuditLogSummary {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n createdAt: r.created_at,\n }\n}\n\n// ── Adapter ────────────────────────────────────────────────────────────────────\n\nexport class MySQLAdapter implements StorageAdapter {\n private pool!: Pool\n\n constructor(private config: StorageConfig) { }\n\n private async q<T extends RowDataPacket>(sql: string, params: (string | number | boolean | null)[] = []): Promise<T[]> {\n const timeout = this.config.statementTimeoutMs\n const [rows] = timeout\n ? await this.pool.query<T[]>({ sql, timeout }, params)\n : await this.pool.query<T[]>(sql, params)\n return rows\n }\n\n private async exec(sql: string, params: (string | number | boolean | null)[] = []): Promise<ResultSetHeader> {\n const timeout = this.config.statementTimeoutMs\n const [result] = timeout\n ? await this.pool.query<ResultSetHeader>({ sql, timeout }, params)\n : await this.pool.query<ResultSetHeader>(sql, params)\n return result\n }\n\n async connect(): Promise<void> {\n const mysql = (await import('mysql2/promise')) as { createPool: typeof import('mysql2/promise').createPool }\n const cfg = this.config\n // Bounded by default (10, matching mysql2's own default — made explicit/tunable). mysql2 has\n // no pool-wide statement timeout; `statementTimeoutMs` is instead applied per-query in q()/exec().\n this.pool = mysql.createPool({\n ...(cfg.uri ? { uri: cfg.uri } : {\n host: cfg.host ?? 'localhost',\n port: cfg.port ?? 3306,\n user: cfg.user ?? 'root',\n password: cfg.password ?? '',\n database: cfg.database ?? 'consenti',\n }),\n connectionLimit: cfg.poolMax ?? 10,\n })\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.pool.end()\n }\n\n async migrate(): Promise<void> {\n // No installations predate this schema, so a fresh connect always creates the full current\n // table set in one pass — no incremental/versioned steps needed. `schema_version` is kept\n // as the hook for whenever a *real* future migration is needed: add `if (version < N)`\n // blocks below the initial creation, the same shape as the removed historical ones.\n const conn: PoolConnection = await this.pool.getConnection()\n try {\n const [tables] = await conn.query<RowDataPacket[]>('SHOW TABLES LIKE ?', ['schema_version'])\n if (tables.length === 0) {\n for (const stmt of SCHEMA_SQL_MYSQL.split(';').map(s => s.trim()).filter(Boolean)) {\n await conn.query(stmt)\n }\n await this.seed(conn)\n await conn.query('INSERT IGNORE INTO schema_version (version) VALUES (1)')\n }\n } finally {\n conn.release()\n }\n }\n\n private async seed(conn: PoolConnection): Promise<void> {\n await conn.query('INSERT IGNORE INTO tenants (id, name, slug) VALUES (?,?,?)', [SEED_TENANT.id, SEED_TENANT.name, SEED_TENANT.slug])\n for (const p of SEED_PERMISSIONS) {\n await conn.query('INSERT IGNORE INTO permissions (id, name, description) VALUES (?,?,?)', [p.id, p.name, p.description])\n }\n for (const r of SEED_ROLES) {\n await conn.query('INSERT IGNORE INTO roles (id, tenant_id, name, description) VALUES (?,?,?,?)', [r.id, r.tenantId, r.name, r.description])\n }\n for (const { roleId, permissionId } of SEED_ROLE_PERMISSIONS) {\n await conn.query('INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?,?)', [roleId, permissionId])\n }\n }\n\n // ── Profiles ─────────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n await this.exec(\n 'INSERT INTO profiles (id, tenant_id, name, default_locale, version, profile_json) VALUES (?,?,?,?,?,?)',\n [id, data.tenantId, data.name, data.defaultLocale, 1, JSON.stringify(data.profileJson)],\n )\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Profile creation failed')\n return mapProfile(row)\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.defaultLocale != null) { sets.push('default_locale=?'); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { sets.push('profile_json=?'); vals.push(JSON.stringify(data.profileJson)) }\n if (data.version != null) { sets.push('version=?'); vals.push(data.version) }\n if (sets.length > 0) {\n vals.push(id)\n await this.exec(`UPDATE profiles SET ${sets.join(',')} WHERE id=?`, vals)\n }\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error(`Profile ${id} not found`)\n return mapProfile(row)\n }\n\n async deleteProfile(id: string): Promise<void> {\n await this.exec('DELETE FROM profiles WHERE id=?', [id])\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=?', [id])\n return rows[0] ? mapProfile(rows[0]) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE tenant_id=? ORDER BY created_at DESC', [tenantId])\n return rows.map(mapProfile)\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n const profiles = await this.getProfiles(tenantId)\n return profiles.find(p =>\n (p.profileJson.complianceGroup === complianceGroup || (p.profileJson as { customComplianceGroup?: string }).customComplianceGroup === complianceGroup) &&\n p.profileJson.isActive\n ) ?? null\n }\n\n // ── Consents ─────────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomConsentId()\n await this.exec(\n 'INSERT INTO consent_records (id, tenant_id, visitor_id, profile_id, locale, consent_json, gpc_detected, source, age_verified, parental_consent_token, tcf_string, signature) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)',\n [id, data.tenantId, data.visitorId, data.profileId, data.locale,\n JSON.stringify(data.consentJson), data.gpcDetected ? 1 : 0, data.source,\n data.ageVerified ? 1 : 0, data.parentalConsentToken ?? null, data.tcfString ?? null, data.signature ?? null],\n )\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Consent creation failed')\n await this.writeHistory(id, data.tenantId, data.visitorId, null, data.consentJson, 'created')\n return mapConsent(row)\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const sets: string[] = ['consent_json=?']\n const vals: (string | number | boolean | null)[] = [JSON.stringify(data.consentJson)]\n if (data.locale != null) { sets.push('locale=?'); vals.push(data.locale) }\n if (data.gpcDetected != null) { sets.push('gpc_detected=?'); vals.push(data.gpcDetected ? 1 : 0) }\n if (data.signature != null) { sets.push('signature=?'); vals.push(data.signature) }\n vals.push(visitorId)\n await this.exec(`UPDATE consent_records SET ${sets.join(',')} WHERE visitor_id=?`, vals)\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE visitor_id=?', [visitorId])\n const row = rows[0]\n if (!row) throw new Error(`Consent for visitor ${visitorId} not found`)\n await this.writeHistory(row.id, row.tenant_id, visitorId, existing.consentJson, data.consentJson, 'updated')\n return mapConsent(row)\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n await this.exec('DELETE FROM consent_history WHERE visitor_id=?', [visitorId])\n await this.exec('DELETE FROM consent_records WHERE visitor_id=?', [visitorId])\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE visitor_id=?', [visitorId])\n return rows[0] ? mapConsent(rows[0]) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n const { where, params } = this.buildConsentWhere(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [rows, countRows] = await Promise.all([\n this.q<RowConsentSummary>(\n `SELECT ${CONSENT_SUMMARY_COLS} ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`,\n [...params, limit, (page - 1) * limit],\n ),\n this.q<RowCount>(`SELECT COUNT(*) AS total ${where}`, params),\n ])\n return { items: rows.map(mapConsentSummary), total: countRows[0]?.total ?? 0, page, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const { where, params } = this.buildConsentWhere(filters)\n const rows = await this.q<RowConsent>(`SELECT * ${where} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapConsent(r)\n }\n\n private buildConsentWhere(f: ConsentFilters): { where: string; params: (string | number | boolean | null)[] } {\n let where = 'FROM consent_records WHERE tenant_id=?'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n if (f.profileId != null) { where += ' AND profile_id=?'; params.push(f.profileId) }\n if (f.from != null) { where += ' AND created_at>=?'; params.push(f.from) }\n if (f.to != null) { where += ' AND created_at<=?'; params.push(f.to) }\n if (f.q) {\n where += \" AND (visitor_id LIKE ? OR profile_id LIKE ? OR locale LIKE ? OR source LIKE ?) ESCAPE '\\\\\\\\'\"\n const like = likePrefix(f.q)\n params.push(like, like, like, like)\n }\n return { where, params }\n }\n\n private async writeHistory(\n consentRecordId: string, tenantId: string, visitorId: string,\n oldJson: ConsentValue | null, newJson: ConsentValue, action: string,\n ): Promise<void> {\n await this.exec(\n 'INSERT INTO consent_history (id, tenant_id, consent_record_id, visitor_id, old_json, new_json, action) VALUES (?,?,?,?,?,?,?)',\n [randomUUID(), tenantId, consentRecordId, visitorId,\n oldJson ? JSON.stringify(oldJson) : null, JSON.stringify(newJson), action],\n )\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = await this.q<RowConsentHistory>('SELECT * FROM consent_history WHERE visitor_id=? ORDER BY created_at ASC', [visitorId])\n return rows.map(mapHistory)\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO notice_shown (id, tenant_id, visitor_id, profile_id, locale) VALUES (?,?,?,?,?)',\n [id, data.tenantId, data.visitorId, data.profileId, data.locale],\n )\n const rows = await this.q<RowNoticeShown>('SELECT * FROM notice_shown WHERE id=?', [id])\n const r = rows[0]!\n return { id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id, locale: r.locale, createdAt: r.created_at }\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n const rows = await this.q<RowNoticeShown>('SELECT * FROM notice_shown WHERE visitor_id=? ORDER BY created_at DESC', [visitorId])\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n profileId: r.profile_id, locale: r.locale, createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ─────────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomVisitorId()\n await this.exec(\n 'INSERT INTO visitors (id, tenant_id, visitor_id, country, region, city, ip_hash, user_agent_hash) VALUES (?,?,?,?,?,?,?,?)',\n [id, data.tenantId, data.visitorId,\n data.country ?? null, data.region ?? null, data.city ?? null,\n data.ipHash ?? null, data.userAgentHash ?? null],\n )\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Visitor creation failed')\n return mapVisitor(row)\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const sets: string[] = ['last_seen=NOW()']\n const vals: (string | number | boolean | null)[] = []\n if (data.country != null) { sets.push('country=?'); vals.push(data.country) }\n if (data.region != null) { sets.push('region=?'); vals.push(data.region) }\n if (data.city != null) { sets.push('city=?'); vals.push(data.city) }\n vals.push(visitorId)\n await this.exec(`UPDATE visitors SET ${sets.join(',')} WHERE visitor_id=?`, vals)\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE visitor_id=?', [visitorId])\n const row = rows[0]\n if (!row) throw new Error(`Visitor ${visitorId} not found`)\n return mapVisitor(row)\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n await this.exec('DELETE FROM visitors WHERE visitor_id=?', [visitorId])\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE visitor_id=?', [visitorId])\n return rows[0] ? mapVisitor(rows[0]) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n let where = 'FROM visitors WHERE tenant_id=?'\n const params: (string | number | boolean | null)[] = [filters.tenantId]\n if (filters.from != null) { where += ' AND first_seen>=?'; params.push(filters.from) }\n if (filters.to != null) { where += ' AND first_seen<=?'; params.push(filters.to) }\n if (filters.q) {\n where += \" AND (visitor_id LIKE ? OR country LIKE ?) ESCAPE '\\\\\\\\'\"\n const like = likePrefix(filters.q)\n params.push(like, like)\n }\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [rows, countRows] = await Promise.all([\n this.q<RowVisitor>(`SELECT * ${where} ORDER BY first_seen DESC LIMIT ? OFFSET ?`, [...params, limit, (page - 1) * limit]),\n this.q<RowCount>(`SELECT COUNT(*) AS total ${where}`, params),\n ])\n return { items: rows.map(mapVisitor), total: countRows[0]?.total ?? 0, page, limit }\n }\n\n // ── Stats ─────────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const [[countRow], [gpcRow], [visitorRow]] = await Promise.all([\n this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=?', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=? AND gpc_detected=1', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*) AS total FROM visitors WHERE tenant_id=?', [tenantId]),\n ])\n const total = countRow?.total ?? 0\n const gpcCount = gpcRow?.total ?? 0\n const visitors = visitorRow?.total ?? 0\n\n const statusRows = await this.q<RowStatus>(\n `SELECT jt.status, COUNT(*) AS count\n FROM consent_records cr\n JOIN JSON_TABLE(cr.consent_json, '$.*' COLUMNS (status VARCHAR(20) PATH '$')) AS jt\n WHERE cr.tenant_id=?\n GROUP BY jt.status`,\n [tenantId],\n )\n let granted = 0, denied = 0, all = 0\n for (const r of statusRows) {\n all += r.count\n if (r.status === 'granted') granted = r.count\n if (r.status === 'denied') denied = r.count\n }\n return {\n totalConsents: total,\n acceptedPct: all > 0 ? Math.round((granted / all) * 100) : 0,\n rejectedPct: all > 0 ? Math.round((denied / all) * 100) : 0,\n totalVisitors: visitors,\n gpcDetectedCount: gpcCount,\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = await this.q<RowCookieStatus>(\n `SELECT jt.cookie_id, jt.status, COUNT(*) AS count\n FROM consent_records cr\n JOIN JSON_TABLE(cr.consent_json, '$.*' COLUMNS (NESTED PATH '$' COLUMNS (\n cookie_id VARCHAR(100) PATH '$.k',\n status VARCHAR(20) PATH '$.v'\n ))) AS jt\n WHERE cr.tenant_id=?\n GROUP BY jt.cookie_id, jt.status`,\n [tenantId],\n )\n const result: CategoryStats = {}\n for (const r of rows) {\n if (!result[r.cookie_id]) result[r.cookie_id] = { granted: 0, denied: 0, objected: 0 }\n const entry = result[r.cookie_id]\n if (entry) {\n if (r.status === 'granted') entry.granted += r.count\n if (r.status === 'denied') entry.denied += r.count\n if (r.status === 'objected') entry.objected += r.count\n }\n }\n return result\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const from = new Date(Date.now() - days * 86400_000).toISOString().slice(0, 10)\n const rows = await this.q<RowDateCount>(\n `SELECT DATE(created_at) AS date, COUNT(*) AS count\n FROM consent_records WHERE tenant_id=? AND created_at>=?\n GROUP BY DATE(created_at) ORDER BY date ASC`,\n [tenantId, from],\n )\n return rows.map(r => ({ date: String(r.date), count: r.count }))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO users (id, tenant_id, name, email, password_hash) VALUES (?,?,?,?,?)',\n [id, data.tenantId, data.name, data.email, data.passwordHash],\n )\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('User creation failed')\n return mapUser(row)\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.email != null) { sets.push('email=?'); vals.push(data.email) }\n if (data.passwordHash != null) { sets.push('password_hash=?'); vals.push(data.passwordHash) }\n if (data.isActive != null) { sets.push('is_active=?'); vals.push(data.isActive ? 1 : 0) }\n if (sets.length > 0) { vals.push(id); await this.exec(`UPDATE users SET ${sets.join(',')} WHERE id=?`, vals) }\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error(`User ${id} not found`)\n return mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.exec('DELETE FROM user_roles WHERE user_id=?', [id])\n await this.exec('DELETE FROM users WHERE id=?', [id])\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=?', [id])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE email=?', [email])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE tenant_id=? ORDER BY created_at DESC', [tenantId])\n return rows.map(mapUser)\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const rows = tenantId\n ? await this.q<RowCount>('SELECT COUNT(*) AS total FROM users WHERE tenant_id=?', [tenantId])\n : await this.q<RowCount>('SELECT COUNT(*) AS total FROM users')\n return rows[0]?.total ?? 0\n }\n\n // ── Roles & permissions ───────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE tenant_id=?', [tenantId])\n return rows.map(mapRole)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO roles (id, tenant_id, name, description) VALUES (?,?,?,?)',\n [id, data.tenantId, data.name, data.description ?? null],\n )\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error('Role creation failed')\n return mapRole(row)\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.description != null) { sets.push('description=?'); vals.push(data.description) }\n if (sets.length > 0) { vals.push(id); await this.exec(`UPDATE roles SET ${sets.join(',')} WHERE id=?`, vals) }\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE id=?', [id])\n const row = rows[0]\n if (!row) throw new Error(`Role ${id} not found`)\n return mapRole(row)\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.exec('DELETE FROM role_permissions WHERE role_id=?', [id])\n await this.exec('DELETE FROM user_roles WHERE role_id=?', [id])\n await this.exec('DELETE FROM roles WHERE id=?', [id])\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = await this.q<RowPermission>('SELECT * FROM permissions ORDER BY name')\n return rows.map(mapPermission)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = await this.q<RowPermission>(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON p.id=rp.permission_id WHERE rp.role_id=?',\n [roleId],\n )\n return rows.map(mapPermission)\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=? AND ur.tenant_id=?',\n [userId, tenantId],\n )\n : await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=?',\n [userId],\n )\n return rows.map(mapRole)\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n await this.exec('INSERT IGNORE INTO user_roles (user_id, role_id, tenant_id) VALUES (?,?,?)', [userId, roleId, tenantId])\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n await this.exec('DELETE FROM user_roles WHERE user_id=? AND role_id=? AND tenant_id=?', [userId, roleId, tenantId])\n } else {\n await this.exec('DELETE FROM user_roles WHERE user_id=? AND role_id=?', [userId, roleId])\n }\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n await this.exec('INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (?,?)', [roleId, permissionId])\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n await this.exec('DELETE FROM role_permissions WHERE role_id=? AND permission_id=?', [roleId, permissionId])\n }\n\n // ── Audit logs ────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n await this.exec(\n 'INSERT INTO audit_logs (id, tenant_id, user_id, action, resource_type, resource_id, old_data, new_data) VALUES (?,?,?,?,?,?,?,?)',\n [randomUUID(), data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null],\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n const { where, params } = this.buildAuditWhere(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [rows, countRows] = await Promise.all([\n this.q<RowAuditLogSummary>(\n `SELECT ${AUDIT_SUMMARY_COLS} ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`,\n [...params, limit, (page - 1) * limit],\n ),\n this.q<RowCount>(`SELECT COUNT(*) AS total ${where}`, params),\n ])\n return { items: rows.map(mapAuditSummary), total: countRows[0]?.total ?? 0, page, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n const rows = await this.q<RowAuditLog>('SELECT * FROM audit_logs WHERE id=?', [id])\n return rows[0] ? mapAuditLog(rows[0]) : null\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const { where, params } = this.buildAuditWhere(filters)\n const rows = await this.q<RowAuditLog>(`SELECT * ${where} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapAuditLog(r)\n }\n\n private buildAuditWhere(f: AuditFilters): { where: string; params: (string | number | boolean | null)[] } {\n let where = 'FROM audit_logs WHERE tenant_id=?'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n if (f.action != null) { where += ' AND action=?'; params.push(f.action) }\n if (f.resourceType != null) { where += ' AND resource_type=?'; params.push(f.resourceType) }\n if (f.from != null) { where += ' AND created_at>=?'; params.push(f.from) }\n if (f.to != null) { where += ' AND created_at<=?'; params.push(f.to) }\n if (f.q) {\n where += \" AND (action LIKE ? OR resource_type LIKE ? OR resource_id LIKE ? OR user_id LIKE ?) ESCAPE '\\\\\\\\'\"\n const like = likePrefix(f.q)\n params.push(like, like, like, like)\n }\n return { where, params }\n }\n\n // ── Stats extensions ───────────────────────────────────────────────────────────\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = await this.q<RowCountry>(\n `SELECT v.country, COUNT(*) AS count FROM consent_records cr\n JOIN visitors v ON v.visitor_id=cr.visitor_id\n WHERE cr.tenant_id=? AND v.country IS NOT NULL\n GROUP BY v.country ORDER BY count DESC LIMIT 50`,\n [tenantId],\n )\n return rows.map(r => ({ country: r.country, count: r.count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const [t] = await this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=?', [tenantId])\n const [d] = await this.q<RowCount>('SELECT COUNT(*) AS total FROM consent_records WHERE tenant_id=? AND gpc_detected=1', [tenantId])\n const total = t?.total ?? 0\n const detected = d?.total ?? 0\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ───────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = await this.q<RowTenant>('SELECT * FROM tenants ORDER BY name ASC', [])\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n // ── API Keys ──────────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RowApiKey): ApiKey {\n return {\n id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active === 1,\n ...(r.created_by !== null ? { createdBy: r.created_by } : {}),\n ...(r.expire_by !== null ? { expireBy: r.expire_by } : {}),\n createdAt: r.created_at,\n ...(r.updated_at !== null ? { updatedAt: r.updated_at } : {}),\n }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n await this.exec(\n 'INSERT INTO api_keys (id, tenant_id, key_hash, name, created_by, expire_by) VALUES (?,?,?,?,?,?)',\n [id, data.tenantId, data.keyHash, data.name, data.createdBy ?? null, data.expireBy ?? null],\n )\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE id=?', [id])\n const r = rows[0]\n if (!r) throw new Error('Failed to create API key')\n return this.mapApiKey(r)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE key_hash=? AND is_active=1', [keyHash])\n const r = rows[0]\n return r ? this.mapApiKey(r) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n await this.exec('UPDATE api_keys SET is_active=0, updated_at=NOW() WHERE id=?', [id])\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n await this.exec(\n 'UPDATE api_keys SET is_active=1, updated_at=NOW(), expire_by=CASE WHEN expire_by <= NOW() THEN NULL ELSE expire_by END WHERE id=?',\n [id],\n )\n }\n\n async deleteApiKey(id: string): Promise<void> {\n await this.exec('DELETE FROM api_keys WHERE id=?', [id])\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE tenant_id=? ORDER BY created_at DESC', [tenantId])\n return rows.map(r => this.mapApiKey(r))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n await this.exec('INSERT INTO tenants (id, name, slug, created_at, updated_at) VALUES (?,?,?,?,?)', [id, data.name, data.slug, now, now])\n const rows = await this.q<RowTenant>('SELECT * FROM tenants WHERE id=?', [id])\n const r = rows[0]\n if (!r) throw new Error('Failed to create tenant')\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const sets: string[] = ['updated_at=?']\n const vals: (string | number)[] = [now]\n if (data.name != null) { sets.push('name=?'); vals.push(data.name) }\n if (data.slug != null) { sets.push('slug=?'); vals.push(data.slug) }\n vals.push(id)\n await this.exec(`UPDATE tenants SET ${sets.join(',')} WHERE id=?`, vals)\n const rows = await this.q<RowTenant>('SELECT * FROM tenants WHERE id=?', [id])\n const r = rows[0]\n if (!r) throw new Error(`Tenant ${id} not found`)\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n const rows = await this.q<RowTenantSettings>(\n 'SELECT allowed_origins_json, admin_allowed_origins_json, setup_completed FROM tenant_settings WHERE tenant_id=?', [tenantId],\n )\n const r = rows[0]\n return r\n ? {\n allowedOrigins: JSON.parse(r.allowed_origins_json) as string[],\n adminAllowedOrigins: JSON.parse(r.admin_allowed_origins_json) as string[],\n setupCompleted: !!r.setup_completed,\n }\n : {}\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const current = await this.getSettings(tenantId)\n const merged = { ...current, ...data }\n await this.exec(\n `INSERT INTO tenant_settings (tenant_id, allowed_origins_json, admin_allowed_origins_json, setup_completed, updated_at) VALUES (?,?,?,?,NOW())\n ON DUPLICATE KEY UPDATE allowed_origins_json=VALUES(allowed_origins_json), admin_allowed_origins_json=VALUES(admin_allowed_origins_json), setup_completed=VALUES(setup_completed), updated_at=NOW()`,\n [tenantId, JSON.stringify(merged.allowedOrigins ?? []), JSON.stringify(merged.adminAllowedOrigins ?? []), merged.setupCompleted ? 1 : 0],\n )\n return this.getSettings(tenantId)\n }\n\n async deleteTenant(id: string): Promise<void> {\n await this.exec('DELETE FROM tenants WHERE id=?', [id])\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const visitorRows = await this.q<{ visitor_id: string } & RowDataPacket>(\n 'SELECT visitor_id FROM consent_records WHERE created_at < ?', [cutoff]\n )\n if (visitorRows.length === 0) return 0\n const ids = visitorRows.map(r => r.visitor_id)\n const placeholders = ids.map(() => '?').join(',')\n await this.exec(`DELETE FROM consent_history WHERE visitor_id IN (${placeholders})`, ids)\n await this.exec(`DELETE FROM consent_records WHERE visitor_id IN (${placeholders})`, ids)\n return ids.length\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const rows = await this.q<RowCount>('SELECT COUNT(*) AS total FROM audit_logs WHERE created_at < ?', [cutoff])\n await this.exec('DELETE FROM audit_logs WHERE created_at < ?', [cutoff])\n return rows[0]?.total ?? 0\n }\n\n // Template methods — not yet implemented for MySQL adapter\n async createConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteConsentTemplate(): Promise<void> { throw new Error('Not implemented') }\n async getConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async getConsentTemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async createUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteUITemplate(): Promise<void> { throw new Error('Not implemented') }\n async getUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async getUITemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyUITemplate(): Promise<never> { throw new Error('Not implemented') }\n\n // Profile summary / analytics — not yet implemented for MySQL adapter\n async listProfilesSummary(): Promise<never[]> { return [] }\n async findProfilesUsingConsentTemplate(): Promise<never[]> { return [] }\n async findProfilesUsingUITemplate(): Promise<never[]> { return [] }\n async getOptInStats(): Promise<never> { throw new Error('Not implemented') }\n}\n","import type { Pool, PoolClient, QueryResult } from 'pg'\nimport { randomUUID, randomProfileId, randomVisitorId, randomConsentId } from '../../utils/crypto'\nimport { SEED_TENANT, SEED_PERMISSIONS, SEED_ROLES, SEED_ROLE_PERMISSIONS, SCHEMA_SQL_POSTGRES } from '../seed-data'\nimport { likePrefix } from '../../utils/sql-search'\nimport type {\n StorageAdapter, StorageConfig, Profile, StoredProfileJson,\n CreateProfileInput, UpdateProfileInput,\n ConsentDbRecord, ConsentSummary, ConsentHistoryEntry, ConsentValue, ConsentFilters,\n CreateConsentInput, UpdateConsentInput,\n Visitor, VisitorFilters, CreateVisitorInput, UpdateVisitorInput,\n AdminUser, CreateUserInput, UpdateUserInput,\n Role, CreateRoleInput, UpdateRoleInput,\n Permission, AuditLog, AuditLogSummary, AuditFilters, CreateAuditLogInput,\n OverviewStats, CategoryStats, TimelineEntry,\n CountryStat, GpcStats, Tenant, ApiKey, CreateApiKeyInput,\n CreateTenantInput, UpdateTenantInput, TenantSettings,\n NoticeShownRecord, CreateNoticeShownInput, PagedResult,\n} from '@consenti/types'\n\n// ── Raw row shapes ─────────────────────────────────────────────────────────────\n\ninterface RowProfile {\n id: string; tenant_id: string; name: string; default_locale: string\n // pg auto-parses json/jsonb columns into JS values — never raw text, so these are typed as\n // the parsed shape, not `string`. Do NOT JSON.parse() these in the mappers below.\n version: number; profile_json: StoredProfileJson\n created_at: string; updated_at: string\n}\ninterface RowConsent {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; consent_json: ConsentValue\n gpc_detected: boolean; source: string; created_at: string; updated_at: string\n age_verified?: boolean; parental_consent_token?: string | null; tcf_string?: string | null\n signature?: string | null\n}\ninterface RowConsentSummary {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; gpc_detected: boolean; source: string\n age_verified?: boolean; created_at: string; updated_at: string\n}\ninterface RowConsentHistory {\n id: string; tenant_id: string; consent_record_id: string; visitor_id: string\n old_json: ConsentValue | null; new_json: ConsentValue; action: string; created_at: string\n}\ninterface RowVisitor {\n id: string; tenant_id: string; visitor_id: string\n country: string | null; region: string | null; city: string | null\n ip_hash: string | null; user_agent_hash: string | null\n first_seen: string; last_seen: string\n}\ninterface RowNoticeShown {\n id: string; tenant_id: string; visitor_id: string; profile_id: string\n locale: string; created_at: string\n}\ninterface RowUser {\n id: string; tenant_id: string; name: string; email: string\n password_hash: string; is_active: boolean; created_at: string; updated_at: string\n totp_secret?: string | null; totp_enabled?: boolean\n}\ninterface RowRole { id: string; tenant_id: string; name: string; description: string | null }\ninterface RowPermission { id: string; name: string; description: string | null }\ninterface RowAuditLog {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null\n old_data: unknown; new_data: unknown; created_at: string\n}\ninterface RowAuditLogSummary {\n id: string; tenant_id: string; user_id: string | null; action: string\n resource_type: string; resource_id: string | null; created_at: string\n}\ninterface RowCount { total: string }\ninterface RowStatus { status: string; count: string }\ninterface RowDateCount { date: string; count: string }\ninterface RowCookieStatus { cookie_id: string; status: string; count: string }\ninterface RowCountry { country: string; count: string }\ninterface RowApiKey {\n id: string; tenant_id: string; key_hash: string; name: string; is_active: boolean\n created_by: string | null; expire_by: string | null; created_at: string; updated_at: string | null\n}\ninterface RowTenant { id: string; name: string; slug: string; created_at: string; updated_at: string }\n\n// ── Mappers ────────────────────────────────────────────────────────────────────\n\nfunction mapProfile(r: RowProfile): Profile {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n defaultLocale: r.default_locale, version: r.version,\n profileJson: r.profile_json,\n createdAt: r.created_at, updatedAt: r.updated_at,\n }\n}\n\nfunction mapConsent(r: RowConsent): ConsentDbRecord {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id,\n locale: r.locale,\n consentJson: r.consent_json,\n gpcDetected: r.gpc_detected, source: r.source as ConsentDbRecord['source'],\n createdAt: r.created_at, updatedAt: r.updated_at,\n ...(r.age_verified != null ? { ageVerified: r.age_verified } : {}),\n ...(r.parental_consent_token != null ? { parentalConsentToken: r.parental_consent_token } : {}),\n ...(r.tcf_string != null ? { tcfString: r.tcf_string } : {}),\n ...(r.signature != null ? { signature: r.signature } : {}),\n }\n}\n\nconst CONSENT_SUMMARY_COLS = 'id, tenant_id, visitor_id, profile_id, locale, gpc_detected, source, age_verified, created_at, updated_at'\n\nfunction mapConsentSummary(r: RowConsentSummary): ConsentSummary {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id,\n locale: r.locale, gpcDetected: r.gpc_detected, source: r.source as ConsentDbRecord['source'],\n createdAt: r.created_at, updatedAt: r.updated_at,\n ...(r.age_verified != null ? { ageVerified: r.age_verified } : {}),\n }\n}\n\nfunction mapHistory(r: RowConsentHistory): ConsentHistoryEntry {\n return {\n id: r.id, tenantId: r.tenant_id, consentRecordId: r.consent_record_id,\n visitorId: r.visitor_id,\n oldJson: r.old_json,\n newJson: r.new_json,\n action: r.action as ConsentHistoryEntry['action'], createdAt: r.created_at,\n }\n}\n\nfunction mapVisitor(r: RowVisitor): Visitor {\n return {\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n ...(r.country != null ? { country: r.country } : {}),\n ...(r.region != null ? { region: r.region } : {}),\n ...(r.city != null ? { city: r.city } : {}),\n ...(r.ip_hash != null ? { ipHash: r.ip_hash } : {}),\n ...(r.user_agent_hash != null ? { userAgentHash: r.user_agent_hash } : {}),\n firstSeen: r.first_seen, lastSeen: r.last_seen,\n }\n}\n\nfunction mapUser(r: RowUser): AdminUser {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name, email: r.email,\n passwordHash: r.password_hash, isActive: r.is_active,\n createdAt: r.created_at, updatedAt: r.updated_at,\n totpEnabled: r.totp_enabled ?? false,\n ...(r.totp_secret != null ? { totpSecret: r.totp_secret } : {}),\n }\n}\n\nfunction mapRole(r: RowRole): Role {\n return {\n id: r.id, tenantId: r.tenant_id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapPermission(r: RowPermission): Permission {\n return {\n id: r.id, name: r.name,\n ...(r.description != null ? { description: r.description } : {}),\n }\n}\n\nfunction mapAuditLog(r: RowAuditLog): AuditLog {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n ...(r.old_data != null ? { oldData: r.old_data } : {}),\n ...(r.new_data != null ? { newData: r.new_data } : {}),\n createdAt: r.created_at,\n }\n}\n\nconst AUDIT_SUMMARY_COLS = 'id, tenant_id, user_id, action, resource_type, resource_id, created_at'\n\nfunction mapAuditSummary(r: RowAuditLogSummary): AuditLogSummary {\n return {\n id: r.id, tenantId: r.tenant_id, action: r.action, resourceType: r.resource_type,\n ...(r.user_id != null ? { userId: r.user_id } : {}),\n ...(r.resource_id != null ? { resourceId: r.resource_id } : {}),\n createdAt: r.created_at,\n }\n}\n\n// ── Adapter ────────────────────────────────────────────────────────────────────\n\nexport class PostgreSQLAdapter implements StorageAdapter {\n private pool!: Pool\n\n constructor(private config: StorageConfig) { }\n\n private async q<T>(sql: string, params: (string | number | boolean | null)[] = []): Promise<T[]> {\n const result: QueryResult<T & object> = await this.pool.query<T & object>(sql, params as unknown[])\n return result.rows\n }\n\n async connect(): Promise<void> {\n type PgModule = { Pool: new (cfg: Record<string, unknown>) => Pool }\n const pg = (await import('pg')) as { default: PgModule }\n const PgPool = pg.default.Pool\n const cfg = this.config\n const poolCfg: Record<string, unknown> = cfg.uri\n ? { connectionString: cfg.uri }\n : {\n host: cfg.host ?? 'localhost',\n port: cfg.port ?? 5432,\n user: cfg.user ?? 'postgres',\n password: cfg.password ?? '',\n database: cfg.database ?? 'consenti',\n }\n // Bounded by default (10, matching pg's own default — made explicit/tunable). Timeouts stay\n // unset unless configured: see StorageConfig.statementTimeoutMs's doc comment for why.\n poolCfg['max'] = cfg.poolMax ?? 10\n if (cfg.statementTimeoutMs != null) poolCfg['statement_timeout'] = cfg.statementTimeoutMs\n if (cfg.idleInTransactionTimeoutMs != null) poolCfg['idle_in_transaction_session_timeout'] = cfg.idleInTransactionTimeoutMs\n this.pool = new PgPool(poolCfg)\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.pool.end()\n }\n\n async migrate(): Promise<void> {\n // No installations predate this schema, so a fresh connect always creates the full current\n // table set in one pass — no incremental/versioned steps needed. `schema_version` is kept\n // as the hook for whenever a *real* future migration is needed: add `if (version < N)`\n // blocks below the initial creation, the same shape as the removed historical ones.\n const client: PoolClient = await this.pool.connect()\n try {\n let version = 0\n try {\n const res = await client.query<{ version: number }>('SELECT version FROM schema_version ORDER BY version DESC LIMIT 1')\n version = res.rows[0]?.version ?? 0\n } catch {\n // schema_version doesn't exist — fresh install\n }\n\n if (version === 0) {\n await client.query('BEGIN')\n try {\n for (const stmt of SCHEMA_SQL_POSTGRES.split(';').map(s => s.trim()).filter(Boolean)) {\n await client.query(stmt)\n }\n await this.seedTx(client)\n await client.query('INSERT INTO schema_version (version) VALUES (1) ON CONFLICT DO NOTHING')\n await client.query('COMMIT')\n } catch (err) {\n await client.query('ROLLBACK')\n throw err\n }\n }\n } finally {\n client.release()\n }\n }\n\n private async seedTx(client: PoolClient): Promise<void> {\n await client.query(\n 'INSERT INTO tenants (id, name, slug) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING',\n [SEED_TENANT.id, SEED_TENANT.name, SEED_TENANT.slug],\n )\n for (const p of SEED_PERMISSIONS) {\n await client.query(\n 'INSERT INTO permissions (id, name, description) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING',\n [p.id, p.name, p.description],\n )\n }\n for (const r of SEED_ROLES) {\n await client.query(\n 'INSERT INTO roles (id, tenant_id, name, description) VALUES ($1,$2,$3,$4) ON CONFLICT DO NOTHING',\n [r.id, r.tenantId, r.name, r.description],\n )\n }\n for (const { roleId, permissionId } of SEED_ROLE_PERMISSIONS) {\n await client.query(\n 'INSERT INTO role_permissions VALUES ($1,$2) ON CONFLICT DO NOTHING',\n [roleId, permissionId],\n )\n }\n }\n\n // ── Profiles ─────────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const id = randomProfileId()\n const rows = await this.q<RowProfile>(\n `INSERT INTO profiles (id, tenant_id, name, default_locale, version, profile_json)\n VALUES ($1,$2,$3,$4,$5,$6::json) RETURNING *`,\n [id, data.tenantId, data.name, data.defaultLocale, 1, JSON.stringify(data.profileJson)],\n )\n const row = rows[0]\n if (!row) throw new Error('Profile creation failed')\n return mapProfile(row)\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const sets: string[] = ['updated_at = NOW()']\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.defaultLocale != null) { sets.push(`default_locale=$${idx++}`); vals.push(data.defaultLocale) }\n if (data.profileJson != null) { sets.push(`profile_json=$${idx++}::json`); vals.push(JSON.stringify(data.profileJson)) }\n if (data.version != null) { sets.push(`version=$${idx++}`); vals.push(data.version) }\n vals.push(id)\n const rows = await this.q<RowProfile>(\n `UPDATE profiles SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`Profile ${id} not found`)\n return mapProfile(row)\n }\n\n async deleteProfile(id: string): Promise<void> {\n await this.q('DELETE FROM profiles WHERE id=$1', [id])\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE id=$1', [id])\n return rows[0] ? mapProfile(rows[0]) : null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n const rows = await this.q<RowProfile>('SELECT * FROM profiles WHERE tenant_id=$1 ORDER BY created_at DESC', [tenantId])\n return rows.map(mapProfile)\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n const profiles = await this.getProfiles(tenantId)\n return profiles.find(p =>\n (p.profileJson.complianceGroup === complianceGroup || (p.profileJson as { customComplianceGroup?: string }).customComplianceGroup === complianceGroup) &&\n p.profileJson.isActive\n ) ?? null\n }\n\n // ── Consents ─────────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const id = randomConsentId()\n const rows = await this.q<RowConsent>(\n `INSERT INTO consent_records (id, tenant_id, visitor_id, profile_id, locale, consent_json, gpc_detected, source, age_verified, parental_consent_token, tcf_string, signature)\n VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8,$9,$10,$11,$12) RETURNING *`,\n [id, data.tenantId, data.visitorId, data.profileId,\n data.locale, JSON.stringify(data.consentJson), data.gpcDetected, data.source,\n data.ageVerified ?? false, data.parentalConsentToken ?? null, data.tcfString ?? null, data.signature ?? null],\n )\n const row = rows[0]\n if (!row) throw new Error('Consent creation failed')\n await this.writeHistory(id, data.tenantId, data.visitorId, null, data.consentJson, 'created')\n return mapConsent(row)\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.getConsent(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n const sets: string[] = ['consent_json=$1::jsonb', 'updated_at=NOW()']\n const vals: (string | number | boolean | null)[] = [JSON.stringify(data.consentJson)]\n let idx = 2\n if (data.locale != null) { sets.push(`locale=$${idx++}`); vals.push(data.locale) }\n if (data.gpcDetected != null) { sets.push(`gpc_detected=$${idx++}`); vals.push(data.gpcDetected) }\n if (data.signature != null) { sets.push(`signature=$${idx++}`); vals.push(data.signature) }\n vals.push(visitorId)\n const rows = await this.q<RowConsent>(\n `UPDATE consent_records SET ${sets.join(',')} WHERE visitor_id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`Consent for visitor ${visitorId} not found`)\n await this.writeHistory(row.id, row.tenant_id, visitorId, existing.consentJson, data.consentJson, 'updated')\n return mapConsent(row)\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n await this.q('DELETE FROM consent_history WHERE visitor_id=$1', [visitorId])\n await this.q('DELETE FROM consent_records WHERE visitor_id=$1', [visitorId])\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n const rows = await this.q<RowConsent>('SELECT * FROM consent_records WHERE visitor_id=$1', [visitorId])\n return rows[0] ? mapConsent(rows[0]) : null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n const { where, params, nextIdx } = this.buildConsentWhere(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [rows, countRows] = await Promise.all([\n this.q<RowConsentSummary>(\n `SELECT ${CONSENT_SUMMARY_COLS} ${where} ORDER BY created_at DESC LIMIT $${nextIdx} OFFSET $${nextIdx + 1}`,\n [...params, limit, (page - 1) * limit],\n ),\n this.q<RowCount>(`SELECT COUNT(*)::int AS total ${where}`, params),\n ])\n return { items: rows.map(mapConsentSummary), total: Number(countRows[0]?.total ?? 0), page, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n const { where, params } = this.buildConsentWhere(filters)\n const rows = await this.q<RowConsent>(`SELECT * ${where} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapConsent(r)\n }\n\n private buildConsentWhere(f: ConsentFilters): { where: string; params: (string | number | boolean | null)[]; nextIdx: number } {\n let where = 'FROM consent_records WHERE tenant_id=$1'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n let idx = 2\n if (f.profileId != null) { where += ` AND profile_id=$${idx++}`; params.push(f.profileId) }\n if (f.from != null) { where += ` AND created_at>=$${idx++}`; params.push(f.from) }\n if (f.to != null) { where += ` AND created_at<=$${idx++}`; params.push(f.to) }\n if (f.q) {\n where += ` AND (visitor_id ILIKE $${idx} OR profile_id ILIKE $${idx} OR locale ILIKE $${idx} OR source ILIKE $${idx}) ESCAPE '\\\\'`\n params.push(likePrefix(f.q))\n idx++\n }\n return { where, params, nextIdx: idx }\n }\n\n private async writeHistory(\n consentRecordId: string, tenantId: string, visitorId: string,\n oldJson: ConsentValue | null, newJson: ConsentValue, action: string,\n ): Promise<void> {\n await this.q(\n `INSERT INTO consent_history (id, tenant_id, consent_record_id, visitor_id, old_json, new_json, action)\n VALUES ($1,$2,$3,$4,$5::jsonb,$6::jsonb,$7)`,\n [randomUUID(), tenantId, consentRecordId, visitorId,\n oldJson ? JSON.stringify(oldJson) : null, JSON.stringify(newJson), action],\n )\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n const rows = await this.q<RowConsentHistory>(\n 'SELECT * FROM consent_history WHERE visitor_id=$1 ORDER BY created_at ASC',\n [visitorId],\n )\n return rows.map(mapHistory)\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const id = randomUUID()\n const rows = await this.q<RowNoticeShown>(\n `INSERT INTO notice_shown (id, tenant_id, visitor_id, profile_id, locale)\n VALUES ($1,$2,$3,$4,$5) RETURNING *`,\n [id, data.tenantId, data.visitorId, data.profileId, data.locale],\n )\n const r = rows[0]!\n return { id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id, profileId: r.profile_id, locale: r.locale, createdAt: r.created_at }\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n const rows = await this.q<RowNoticeShown>(\n 'SELECT * FROM notice_shown WHERE visitor_id=$1 ORDER BY created_at DESC',\n [visitorId],\n )\n return rows.map(r => ({\n id: r.id, tenantId: r.tenant_id, visitorId: r.visitor_id,\n profileId: r.profile_id, locale: r.locale, createdAt: r.created_at,\n }))\n }\n\n // ── Visitors ─────────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const id = randomVisitorId()\n const rows = await this.q<RowVisitor>(\n `INSERT INTO visitors (id, tenant_id, visitor_id, country, region, city, ip_hash, user_agent_hash)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING *`,\n [id, data.tenantId, data.visitorId,\n data.country ?? null, data.region ?? null, data.city ?? null,\n data.ipHash ?? null, data.userAgentHash ?? null],\n )\n const row = rows[0]\n if (!row) throw new Error('Visitor creation failed')\n return mapVisitor(row)\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const sets: string[] = ['last_seen=NOW()']\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.country != null) { sets.push(`country=$${idx++}`); vals.push(data.country) }\n if (data.region != null) { sets.push(`region=$${idx++}`); vals.push(data.region) }\n if (data.city != null) { sets.push(`city=$${idx++}`); vals.push(data.city) }\n vals.push(visitorId)\n const rows = await this.q<RowVisitor>(\n `UPDATE visitors SET ${sets.join(',')} WHERE visitor_id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`Visitor ${visitorId} not found`)\n return mapVisitor(row)\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n await this.q('DELETE FROM visitors WHERE visitor_id=$1', [visitorId])\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n const rows = await this.q<RowVisitor>('SELECT * FROM visitors WHERE visitor_id=$1', [visitorId])\n return rows[0] ? mapVisitor(rows[0]) : null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n let where = 'FROM visitors WHERE tenant_id=$1'\n const params: (string | number | boolean | null)[] = [filters.tenantId]\n let idx = 2\n if (filters.from != null) { where += ` AND first_seen>=$${idx++}`; params.push(filters.from) }\n if (filters.to != null) { where += ` AND first_seen<=$${idx++}`; params.push(filters.to) }\n if (filters.q) {\n where += ` AND (visitor_id ILIKE $${idx} OR country ILIKE $${idx}) ESCAPE '\\\\'`\n params.push(likePrefix(filters.q))\n idx++\n }\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [rows, countRows] = await Promise.all([\n this.q<RowVisitor>(\n `SELECT * ${where} ORDER BY first_seen DESC LIMIT $${idx} OFFSET $${idx + 1}`,\n [...params, limit, (page - 1) * limit],\n ),\n this.q<RowCount>(`SELECT COUNT(*)::int AS total ${where}`, params),\n ])\n return { items: rows.map(mapVisitor), total: Number(countRows[0]?.total ?? 0), page, limit }\n }\n\n // ── Stats ─────────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const [[countRow], [gpcRow], [visitorRow]] = await Promise.all([\n this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1 AND gpc_detected=true', [tenantId]),\n this.q<RowCount>('SELECT COUNT(*)::int AS total FROM visitors WHERE tenant_id=$1', [tenantId]),\n ])\n const total = Number(countRow?.total ?? 0)\n const gpcCount = Number(gpcRow?.total ?? 0)\n const visitors = Number(visitorRow?.total ?? 0)\n\n const statusRows = await this.q<RowStatus>(\n `SELECT val AS status, COUNT(*)::int AS count\n FROM consent_records cr,\n jsonb_each_text(cr.consent_json) AS kv(key, val)\n WHERE cr.tenant_id=$1\n GROUP BY val`,\n [tenantId],\n )\n let granted = 0, denied = 0, all = 0\n for (const r of statusRows) {\n all += Number(r.count)\n if (r.status === 'granted') granted = Number(r.count)\n if (r.status === 'denied') denied = Number(r.count)\n }\n return {\n totalConsents: total,\n acceptedPct: all > 0 ? Math.round((granted / all) * 100) : 0,\n rejectedPct: all > 0 ? Math.round((denied / all) * 100) : 0,\n totalVisitors: visitors,\n gpcDetectedCount: gpcCount,\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const rows = await this.q<RowCookieStatus>(\n `SELECT kv.key AS cookie_id, kv.val AS status, COUNT(*)::int AS count\n FROM consent_records cr,\n jsonb_each_text(cr.consent_json) AS kv(key, val)\n WHERE cr.tenant_id=$1\n GROUP BY kv.key, kv.val`,\n [tenantId],\n )\n const result: CategoryStats = {}\n for (const r of rows) {\n if (!result[r.cookie_id]) result[r.cookie_id] = { granted: 0, denied: 0, objected: 0 }\n const entry = result[r.cookie_id]\n if (entry) {\n if (r.status === 'granted') entry.granted += Number(r.count)\n if (r.status === 'denied') entry.denied += Number(r.count)\n if (r.status === 'objected') entry.objected += Number(r.count)\n }\n }\n return result\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const from = new Date(Date.now() - days * 86400_000).toISOString()\n const rows = await this.q<RowDateCount>(\n `SELECT to_char(created_at, 'YYYY-MM-DD') AS date, COUNT(*)::int AS count\n FROM consent_records\n WHERE tenant_id=$1 AND created_at>=$2\n GROUP BY to_char(created_at, 'YYYY-MM-DD')\n ORDER BY date ASC`,\n [tenantId, from],\n )\n return rows.map(r => ({ date: r.date, count: Number(r.count) }))\n }\n\n // ── Users ─────────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const id = randomUUID()\n const rows = await this.q<RowUser>(\n 'INSERT INTO users (id, tenant_id, name, email, password_hash) VALUES ($1,$2,$3,$4,$5) RETURNING *',\n [id, data.tenantId, data.name, data.email, data.passwordHash],\n )\n const row = rows[0]\n if (!row) throw new Error('User creation failed')\n return mapUser(row)\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const sets: string[] = ['updated_at=NOW()']\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.email != null) { sets.push(`email=$${idx++}`); vals.push(data.email) }\n if (data.passwordHash != null) { sets.push(`password_hash=$${idx++}`); vals.push(data.passwordHash) }\n if (data.isActive != null) { sets.push(`is_active=$${idx++}`); vals.push(data.isActive) }\n vals.push(id)\n const rows = await this.q<RowUser>(\n `UPDATE users SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`,\n vals,\n )\n const row = rows[0]\n if (!row) throw new Error(`User ${id} not found`)\n return mapUser(row)\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.q('DELETE FROM user_roles WHERE user_id=$1', [id])\n await this.q('DELETE FROM users WHERE id=$1', [id])\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE id=$1', [id])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE email=$1', [email])\n return rows[0] ? mapUser(rows[0]) : null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n const rows = await this.q<RowUser>('SELECT * FROM users WHERE tenant_id=$1 ORDER BY created_at DESC', [tenantId])\n return rows.map(mapUser)\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n const rows = tenantId\n ? await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM users WHERE tenant_id=$1', [tenantId])\n : await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM users')\n return Number(rows[0]?.total ?? 0)\n }\n\n // ── Roles & permissions ───────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE tenant_id=$1', [tenantId])\n return rows.map(mapRole)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const id = randomUUID()\n const rows = await this.q<RowRole>(\n 'INSERT INTO roles (id, tenant_id, name, description) VALUES ($1,$2,$3,$4) RETURNING *',\n [id, data.tenantId, data.name, data.description ?? null],\n )\n const row = rows[0]\n if (!row) throw new Error('Role creation failed')\n return mapRole(row)\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const sets: string[] = []\n const vals: (string | number | boolean | null)[] = []\n let idx = 1\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.description != null) { sets.push(`description=$${idx++}`); vals.push(data.description) }\n if (sets.length === 0) {\n const rows = await this.q<RowRole>('SELECT * FROM roles WHERE id=$1', [id])\n const row = rows[0]\n if (!row) throw new Error(`Role ${id} not found`)\n return mapRole(row)\n }\n vals.push(id)\n const rows = await this.q<RowRole>(`UPDATE roles SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`, vals)\n const row = rows[0]\n if (!row) throw new Error(`Role ${id} not found`)\n return mapRole(row)\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.q('DELETE FROM role_permissions WHERE role_id=$1', [id])\n await this.q('DELETE FROM user_roles WHERE role_id=$1', [id])\n await this.q('DELETE FROM roles WHERE id=$1', [id])\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n const rows = await this.q<RowPermission>('SELECT * FROM permissions ORDER BY name')\n return rows.map(mapPermission)\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const rows = await this.q<RowPermission>(\n 'SELECT p.* FROM permissions p JOIN role_permissions rp ON p.id=rp.permission_id WHERE rp.role_id=$1',\n [roleId],\n )\n return rows.map(mapPermission)\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n const rows = tenantId\n ? await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=$1 AND ur.tenant_id=$2',\n [userId, tenantId],\n )\n : await this.q<RowRole>(\n 'SELECT r.* FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=$1',\n [userId],\n )\n return rows.map(mapRole)\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n await this.q('INSERT INTO user_roles (user_id, role_id, tenant_id) VALUES ($1,$2,$3) ON CONFLICT DO NOTHING', [userId, roleId, tenantId])\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n if (tenantId) {\n await this.q('DELETE FROM user_roles WHERE user_id=$1 AND role_id=$2 AND tenant_id=$3', [userId, roleId, tenantId])\n } else {\n await this.q('DELETE FROM user_roles WHERE user_id=$1 AND role_id=$2', [userId, roleId])\n }\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n await this.q('INSERT INTO role_permissions (role_id, permission_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [roleId, permissionId])\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n await this.q('DELETE FROM role_permissions WHERE role_id=$1 AND permission_id=$2', [roleId, permissionId])\n }\n\n // ── Audit logs ────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n await this.q(\n `INSERT INTO audit_logs (id, tenant_id, user_id, action, resource_type, resource_id, old_data, new_data)\n VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8::jsonb)`,\n [randomUUID(), data.tenantId, data.userId ?? null, data.action, data.resourceType,\n data.resourceId ?? null,\n data.oldData != null ? JSON.stringify(data.oldData) : null,\n data.newData != null ? JSON.stringify(data.newData) : null],\n )\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n const { where, params, nextIdx } = this.buildAuditWhere(filters)\n const page = filters.page ?? 1\n const limit = filters.limit ?? 50\n const [rows, countRows] = await Promise.all([\n this.q<RowAuditLogSummary>(\n `SELECT ${AUDIT_SUMMARY_COLS} ${where} ORDER BY created_at DESC LIMIT $${nextIdx} OFFSET $${nextIdx + 1}`,\n [...params, limit, (page - 1) * limit],\n ),\n this.q<RowCount>(`SELECT COUNT(*)::int AS total ${where}`, params),\n ])\n return { items: rows.map(mapAuditSummary), total: Number(countRows[0]?.total ?? 0), page, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n const rows = await this.q<RowAuditLog>('SELECT * FROM audit_logs WHERE id=$1', [id])\n return rows[0] ? mapAuditLog(rows[0]) : null\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n const { where, params } = this.buildAuditWhere(filters)\n const rows = await this.q<RowAuditLog>(`SELECT * ${where} ORDER BY created_at ASC`, params)\n for (const r of rows) yield mapAuditLog(r)\n }\n\n private buildAuditWhere(f: AuditFilters): { where: string; params: (string | number | boolean | null)[]; nextIdx: number } {\n let where = 'FROM audit_logs WHERE tenant_id=$1'\n const params: (string | number | boolean | null)[] = [f.tenantId]\n let idx = 2\n if (f.action != null) { where += ` AND action=$${idx++}`; params.push(f.action) }\n if (f.resourceType != null) { where += ` AND resource_type=$${idx++}`; params.push(f.resourceType) }\n if (f.from != null) { where += ` AND created_at>=$${idx++}`; params.push(f.from) }\n if (f.to != null) { where += ` AND created_at<=$${idx++}`; params.push(f.to) }\n if (f.q) {\n where += ` AND (action ILIKE $${idx} OR resource_type ILIKE $${idx} OR resource_id ILIKE $${idx} OR user_id ILIKE $${idx}) ESCAPE '\\\\'`\n params.push(likePrefix(f.q))\n idx++\n }\n return { where, params, nextIdx: idx }\n }\n\n // ── Stats extensions ───────────────────────────────────────────────────────────\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const rows = await this.q<RowCountry>(\n `SELECT v.country, COUNT(*)::int AS count FROM consent_records cr\n JOIN visitors v ON v.visitor_id=cr.visitor_id\n WHERE cr.tenant_id=$1 AND v.country IS NOT NULL\n GROUP BY v.country ORDER BY count DESC LIMIT 50`,\n [tenantId],\n )\n return rows.map(r => ({ country: r.country, count: Number(r.count) }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const [t] = await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1', [tenantId])\n const [d] = await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM consent_records WHERE tenant_id=$1 AND gpc_detected=TRUE', [tenantId])\n const total = Number(t?.total ?? 0)\n const detected = Number(d?.total ?? 0)\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ───────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n const rows = await this.q<RowTenant>('SELECT * FROM tenants ORDER BY name ASC', [])\n return rows.map(r => ({ id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }))\n }\n\n // ── API Keys ──────────────────────────────────────────────────────────────────\n\n private mapApiKey(r: RowApiKey): ApiKey {\n return {\n id: r.id, tenantId: r.tenant_id, keyHash: r.key_hash, name: r.name, isActive: r.is_active,\n ...(r.created_by !== null ? { createdBy: r.created_by } : {}),\n ...(r.expire_by !== null ? { expireBy: r.expire_by } : {}),\n createdAt: r.created_at,\n ...(r.updated_at !== null ? { updatedAt: r.updated_at } : {}),\n }\n }\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const id = randomUUID()\n const rows = await this.q<RowApiKey>(\n 'INSERT INTO api_keys (id, tenant_id, key_hash, name, created_by, expire_by) VALUES ($1,$2,$3,$4,$5,$6) RETURNING *',\n [id, data.tenantId, data.keyHash, data.name, data.createdBy ?? null, data.expireBy ?? null],\n )\n const r = rows[0]\n if (!r) throw new Error('Failed to create API key')\n return this.mapApiKey(r)\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE key_hash=$1 AND is_active=TRUE', [keyHash])\n const r = rows[0]\n return r ? this.mapApiKey(r) : null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n await this.q('UPDATE api_keys SET is_active=FALSE, updated_at=NOW() WHERE id=$1', [id])\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n await this.q(\n \"UPDATE api_keys SET is_active=TRUE, updated_at=NOW(), expire_by=CASE WHEN expire_by <= NOW() THEN NULL ELSE expire_by END WHERE id=$1\",\n [id],\n )\n }\n\n async deleteApiKey(id: string): Promise<void> {\n await this.q('DELETE FROM api_keys WHERE id=$1', [id])\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n const rows = await this.q<RowApiKey>('SELECT * FROM api_keys WHERE tenant_id=$1 ORDER BY created_at DESC', [tenantId])\n return rows.map(r => this.mapApiKey(r))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const id = randomUUID()\n const now = new Date().toISOString()\n const rows = await this.q<RowTenant>(\n 'INSERT INTO tenants (id, name, slug, created_at, updated_at) VALUES ($1,$2,$3,$4,$5) RETURNING *',\n [id, data.name, data.slug, now, now],\n )\n const r = rows[0]\n if (!r) throw new Error('Failed to create tenant')\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const sets: string[] = ['updated_at=$1']\n const vals: (string | number)[] = [now]\n let idx = 2\n if (data.name != null) { sets.push(`name=$${idx++}`); vals.push(data.name) }\n if (data.slug != null) { sets.push(`slug=$${idx++}`); vals.push(data.slug) }\n vals.push(id)\n const rows = await this.q<RowTenant>(\n `UPDATE tenants SET ${sets.join(',')} WHERE id=$${idx} RETURNING *`, vals,\n )\n const r = rows[0]\n if (!r) throw new Error(`Tenant ${id} not found`)\n return { id: r.id, name: r.name, slug: r.slug, createdAt: r.created_at, updatedAt: r.updated_at }\n }\n\n async deleteTenant(id: string): Promise<void> {\n await this.q('DELETE FROM tenants WHERE id=$1', [id])\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n const rows = await this.q<{ allowed_origins_json: string[]; admin_allowed_origins_json: string[]; setup_completed: boolean }>(\n 'SELECT allowed_origins_json, admin_allowed_origins_json, setup_completed FROM tenant_settings WHERE tenant_id=$1', [tenantId],\n )\n const r = rows[0]\n return r ? { allowedOrigins: r.allowed_origins_json, adminAllowedOrigins: r.admin_allowed_origins_json, setupCompleted: r.setup_completed } : {}\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const current = await this.getSettings(tenantId)\n const merged = { ...current, ...data }\n await this.q(\n `INSERT INTO tenant_settings (tenant_id, allowed_origins_json, admin_allowed_origins_json, setup_completed, updated_at) VALUES ($1,$2::jsonb,$3::jsonb,$4,NOW())\n ON CONFLICT (tenant_id) DO UPDATE SET allowed_origins_json=$2::jsonb, admin_allowed_origins_json=$3::jsonb, setup_completed=$4, updated_at=NOW()`,\n [tenantId, JSON.stringify(merged.allowedOrigins ?? []), JSON.stringify(merged.adminAllowedOrigins ?? []), merged.setupCompleted ?? false],\n )\n return this.getSettings(tenantId)\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const visitorRows = await this.q<{ visitor_id: string }>(\n 'SELECT visitor_id FROM consent_records WHERE created_at < $1', [cutoff]\n )\n if (visitorRows.length === 0) return 0\n const ids = visitorRows.map(r => r.visitor_id)\n const placeholders = ids.map((_: string, i: number) => `$${i + 1}`).join(',')\n await this.q(`DELETE FROM consent_history WHERE visitor_id IN (${placeholders})`, ids)\n await this.q(`DELETE FROM consent_records WHERE visitor_id IN (${placeholders})`, ids)\n return ids.length\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const rows = await this.q<RowCount>('SELECT COUNT(*)::int AS total FROM audit_logs WHERE created_at < $1', [cutoff])\n await this.q('DELETE FROM audit_logs WHERE created_at < $1', [cutoff])\n return Number(rows[0]?.total ?? 0)\n }\n\n // Template methods — not yet implemented for PostgreSQL adapter\n async createConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteConsentTemplate(): Promise<void> { throw new Error('Not implemented') }\n async getConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async getConsentTemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyConsentTemplate(): Promise<never> { throw new Error('Not implemented') }\n async createUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async updateUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async deleteUITemplate(): Promise<void> { throw new Error('Not implemented') }\n async getUITemplate(): Promise<never> { throw new Error('Not implemented') }\n async getUITemplates(): Promise<never> { throw new Error('Not implemented') }\n async copyUITemplate(): Promise<never> { throw new Error('Not implemented') }\n\n // Profile summary / analytics — not yet implemented for PostgreSQL adapter\n async listProfilesSummary(): Promise<never[]> { return [] }\n async findProfilesUsingConsentTemplate(): Promise<never[]> { return [] }\n async findProfilesUsingUITemplate(): Promise<never[]> { return [] }\n async getOptInStats(): Promise<never> { throw new Error('Not implemented') }\n}\n","import { readFile, writeFile, rename, mkdir } from 'node:fs/promises'\nimport { randomUUID } from 'node:crypto'\nimport { randomProfileId, randomVisitorId, randomConsentId, randomConsentTemplateId, randomUITemplateId } from '../../utils/crypto'\nimport type {\n StorageAdapter,\n StorageConfig,\n Profile,\n StoredProfileJson,\n ProfileSummary,\n OptInStats,\n OptInFilters,\n ComplianceGroupId,\n CreateProfileInput,\n UpdateProfileInput,\n ConsentDbRecord,\n ConsentSummary,\n ConsentHistoryEntry,\n ConsentValue,\n CreateConsentInput,\n UpdateConsentInput,\n ConsentFilters,\n Visitor,\n VisitorFilters,\n CreateVisitorInput,\n UpdateVisitorInput,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n CreateRoleInput,\n UpdateRoleInput,\n Permission,\n AuditLog,\n AuditLogSummary,\n AuditFilters,\n CreateAuditLogInput,\n OverviewStats,\n CategoryStats,\n TimelineEntry,\n CountryStat,\n GpcStats,\n Tenant,\n ApiKey,\n CreateApiKeyInput,\n CreateTenantInput,\n UpdateTenantInput,\n ServerConsentTemplate,\n ServerUITemplate,\n CreateConsentTemplateInput,\n UpdateConsentTemplateInput,\n CreateUITemplateInput,\n UpdateUITemplateInput,\n TenantSettings,\n NoticeShownRecord,\n CreateNoticeShownInput,\n PagedResult,\n} from '@consenti/types'\nimport { resolveStoragePaths } from '../../utils/storage-path'\nimport {\n SEED_TENANT,\n SEED_PERMISSIONS,\n SEED_ROLES,\n SEED_ROLE_PERMISSIONS,\n} from '../seed-data'\n\nconst MAX_PAGE_SIZE = 500\n\ninterface UserRole { userId: string; roleId: string; tenantId: string }\ninterface RolePermission { roleId: string; permissionId: string }\n\ninterface JsonDb {\n _version: number\n tenants: Tenant[]\n profiles: Profile[]\n consents: ConsentDbRecord[]\n consent_history: ConsentHistoryEntry[]\n visitors: Visitor[]\n users: AdminUser[]\n roles: Role[]\n permissions: Permission[]\n user_roles: UserRole[]\n role_permissions: RolePermission[]\n audit_logs: AuditLog[]\n consent_templates: ServerConsentTemplate[]\n ui_templates: ServerUITemplate[]\n api_keys: ApiKey[]\n settings: Record<string, TenantSettings>\n notice_shown: NoticeShownRecord[]\n}\n\nfunction emptyDb(): JsonDb {\n return {\n _version: 1,\n tenants: [],\n profiles: [],\n consents: [],\n consent_history: [],\n visitors: [],\n users: [],\n roles: [],\n permissions: [],\n user_roles: [],\n role_permissions: [],\n audit_logs: [],\n consent_templates: [],\n ui_templates: [],\n api_keys: [],\n settings: {},\n notice_shown: [],\n }\n}\n\nfunction page<T>(arr: T[], pageNum: number, limit: number): T[] {\n const l = Math.min(limit, MAX_PAGE_SIZE)\n const offset = (pageNum - 1) * l\n return arr.slice(offset, offset + l)\n}\n\n// The JSON driver holds everything in memory, so column projection saves no I/O here — these\n// exist purely to keep the wire response shape identical to the other 6 adapters.\nfunction toConsentSummary(c: ConsentDbRecord): ConsentSummary {\n const { id, tenantId, visitorId, profileId, locale, gpcDetected, source, ageVerified, createdAt, updatedAt } = c\n return { id, tenantId, visitorId, profileId, locale, gpcDetected, source, ...(ageVerified != null ? { ageVerified } : {}), createdAt, updatedAt }\n}\n\nfunction toAuditSummary(l: AuditLog): AuditLogSummary {\n const { id, tenantId, userId, action, resourceType, resourceId, createdAt } = l\n return { id, tenantId, action, resourceType, createdAt, ...(userId != null ? { userId } : {}), ...(resourceId != null ? { resourceId } : {}) }\n}\n\nexport class JsonFileAdapter implements StorageAdapter {\n private db: JsonDb = emptyDb()\n private filePath = ''\n // Steno-equivalent: serialised writes via promise chain.\n // Old settled promises are GC'd on reassignment — no memory leak.\n private writeQueue: Promise<void> = Promise.resolve()\n\n constructor(private config: StorageConfig) {}\n\n private scheduleWrite(): void {\n this.writeQueue = this.writeQueue\n .then(async () => {\n await mkdir(this.filePath.replace(/\\/[^/]+$/, ''), { recursive: true })\n const tmp = `${this.filePath}.tmp`\n await writeFile(tmp, JSON.stringify(this.db), 'utf8')\n await rename(tmp, this.filePath)\n })\n .catch((err: unknown) => { console.warn('[Consenti] JSON store write failed:', err) })\n }\n\n async connect(): Promise<void> {\n const { dbPath } = resolveStoragePaths(this.config.path ?? './consenti-data', 'json')\n this.filePath = dbPath\n try {\n const raw = await readFile(this.filePath, 'utf8')\n this.db = { ...emptyDb(), ...JSON.parse(raw) as JsonDb }\n } catch {\n this.db = emptyDb()\n }\n await this.migrate()\n }\n\n async disconnect(): Promise<void> {\n await this.writeQueue\n }\n\n async migrate(): Promise<void> {\n this.seed()\n }\n\n private seed(): void {\n const now = new Date().toISOString()\n const { id, name, slug } = SEED_TENANT\n if (!this.db.tenants.find(t => t.id === id)) {\n this.db.tenants.push({ id, name, slug, createdAt: now, updatedAt: now })\n }\n for (const p of SEED_PERMISSIONS) {\n if (!this.db.permissions.find(x => x.id === p.id)) {\n this.db.permissions.push({ id: p.id, name: p.name, description: p.description })\n }\n }\n for (const r of SEED_ROLES) {\n if (!this.db.roles.find(x => x.id === r.id)) {\n this.db.roles.push({ id: r.id, tenantId: r.tenantId, name: r.name, description: r.description })\n }\n }\n for (const rp of SEED_ROLE_PERMISSIONS) {\n if (!this.db.role_permissions.find(x => x.roleId === rp.roleId && x.permissionId === rp.permissionId)) {\n this.db.role_permissions.push(rp)\n }\n }\n this.scheduleWrite()\n }\n\n // ── Profiles ──────────────────────────────────────────────────────────────\n\n async createProfile(data: CreateProfileInput): Promise<Profile> {\n const now = new Date().toISOString()\n const id = randomProfileId()\n const profile: Profile = {\n id,\n tenantId: data.tenantId,\n name: data.name,\n defaultLocale: data.defaultLocale,\n version: 1,\n profileJson: data.profileJson as StoredProfileJson,\n createdAt: now,\n updatedAt: now,\n }\n this.db.profiles.push(profile)\n this.scheduleWrite()\n return profile\n }\n\n async updateProfile(id: string, data: UpdateProfileInput): Promise<Profile> {\n const idx = this.db.profiles.findIndex(p => p.id === id)\n if (idx === -1) throw new Error(`Profile ${id} not found`)\n const existing = this.db.profiles[idx]!\n const updated: Profile = {\n ...existing,\n ...(data.name != null && { name: data.name }),\n ...(data.defaultLocale != null && { defaultLocale: data.defaultLocale }),\n ...(data.profileJson != null && { profileJson: data.profileJson as StoredProfileJson }),\n ...(data.version != null && { version: data.version }),\n updatedAt: new Date().toISOString(),\n }\n this.db.profiles[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteProfile(id: string): Promise<void> {\n this.db.profiles = this.db.profiles.filter(p => p.id !== id)\n this.scheduleWrite()\n }\n\n async getProfile(id: string): Promise<Profile | null> {\n return this.db.profiles.find(p => p.id === id) ?? null\n }\n\n async getProfiles(tenantId: string): Promise<Profile[]> {\n return this.db.profiles.filter(p => p.tenantId === tenantId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n }\n\n async findActiveProfileByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n return this.db.profiles.find(p =>\n p.tenantId === tenantId &&\n (p.profileJson.complianceGroup === complianceGroup || (p.profileJson as { customComplianceGroup?: string }).customComplianceGroup === complianceGroup) &&\n p.profileJson.isActive\n ) ?? null\n }\n\n // ── Consents ──────────────────────────────────────────────────────────────\n\n async createConsent(data: CreateConsentInput): Promise<ConsentDbRecord> {\n const now = new Date().toISOString()\n const id = randomConsentId()\n const record: ConsentDbRecord = {\n id,\n tenantId: data.tenantId,\n visitorId: data.visitorId,\n profileId: data.profileId,\n locale: data.locale,\n consentJson: data.consentJson as ConsentValue,\n gpcDetected: data.gpcDetected ?? false,\n source: data.source as ConsentDbRecord['source'],\n ...(data.ageVerified != null && { ageVerified: data.ageVerified }),\n ...(data.parentalConsentToken != null && { parentalConsentToken: data.parentalConsentToken }),\n ...(data.tcfString != null && { tcfString: data.tcfString }),\n ...(data.signature != null && { signature: data.signature }),\n createdAt: now,\n updatedAt: now,\n }\n this.db.consents.push(record)\n this.db.consent_history.push({\n id: randomUUID(),\n tenantId: data.tenantId,\n consentRecordId: id,\n visitorId: data.visitorId,\n oldJson: null,\n newJson: data.consentJson as ConsentValue,\n action: 'created',\n createdAt: now,\n })\n this.scheduleWrite()\n return record\n }\n\n async updateConsent(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n const idx = this.db.consents.findIndex(c => c.visitorId === visitorId)\n if (idx === -1) throw new Error(`Consent for visitor ${visitorId} not found`)\n const existing = this.db.consents[idx]!\n const now = new Date().toISOString()\n const updated: ConsentDbRecord = {\n ...existing,\n consentJson: data.consentJson as ConsentValue,\n ...(data.locale != null && { locale: data.locale }),\n ...(data.gpcDetected != null && { gpcDetected: data.gpcDetected }),\n ...(data.signature != null && { signature: data.signature }),\n updatedAt: now,\n }\n this.db.consents[idx] = updated\n this.db.consent_history.push({\n id: randomUUID(),\n tenantId: existing.tenantId,\n consentRecordId: existing.id,\n visitorId,\n oldJson: existing.consentJson,\n newJson: data.consentJson as ConsentValue,\n action: 'updated',\n createdAt: now,\n })\n this.scheduleWrite()\n return updated\n }\n\n async deleteConsent(visitorId: string): Promise<void> {\n this.db.consent_history = this.db.consent_history.filter(h => h.visitorId !== visitorId)\n this.db.consents = this.db.consents.filter(c => c.visitorId !== visitorId)\n this.scheduleWrite()\n }\n\n async getConsent(visitorId: string): Promise<ConsentDbRecord | null> {\n return this.db.consents.find(c => c.visitorId === visitorId) ?? null\n }\n\n async getConsents(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n let rows = this.db.consents.filter(c => c.tenantId === filters.tenantId)\n if (filters.profileId) rows = rows.filter(c => c.profileId === filters.profileId)\n if (filters.from) rows = rows.filter(c => c.createdAt >= filters.from!)\n if (filters.to) rows = rows.filter(c => c.createdAt <= filters.to!)\n if (filters.q) {\n // Prefix match, not substring — kept consistent with every other adapter's search\n // semantics (which use a leading-wildcard-free query so they stay index-backed).\n const q = filters.q.toLowerCase()\n rows = rows.filter(c =>\n c.visitorId.toLowerCase().startsWith(q) ||\n c.profileId.toLowerCase().startsWith(q) ||\n c.locale.toLowerCase().startsWith(q) ||\n c.source.toLowerCase().startsWith(q))\n }\n rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n const page_ = filters.page ?? 1\n const limit = filters.limit ?? 50\n return { items: page(rows, page_, limit).map(toConsentSummary), total: rows.length, page: page_, limit }\n }\n\n async *streamConsents(filters: ConsentFilters): AsyncIterable<ConsentDbRecord> {\n for (const c of this.db.consents.filter(c => c.tenantId === filters.tenantId)) yield c\n }\n\n async getConsentHistory(visitorId: string): Promise<ConsentHistoryEntry[]> {\n return this.db.consent_history\n .filter(h => h.visitorId === visitorId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n }\n\n async createNoticeShown(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n const record: NoticeShownRecord = {\n id: randomUUID(),\n tenantId: data.tenantId,\n visitorId: data.visitorId,\n profileId: data.profileId,\n locale: data.locale,\n createdAt: new Date().toISOString(),\n }\n this.db.notice_shown.push(record)\n this.scheduleWrite()\n return record\n }\n\n async getNoticeShownForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n return this.db.notice_shown\n .filter(n => n.visitorId === visitorId)\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n }\n\n // ── Visitors ──────────────────────────────────────────────────────────────\n\n async createVisitor(data: CreateVisitorInput): Promise<Visitor> {\n const now = new Date().toISOString()\n const visitor: Visitor = {\n id: randomVisitorId(),\n tenantId: data.tenantId,\n visitorId: data.visitorId,\n firstSeen: now,\n lastSeen: now,\n ...(data.country != null && { country: data.country }),\n ...(data.region != null && { region: data.region }),\n ...(data.city != null && { city: data.city }),\n ...(data.ipHash != null && { ipHash: data.ipHash }),\n ...(data.userAgentHash != null && { userAgentHash: data.userAgentHash }),\n }\n this.db.visitors.push(visitor)\n this.scheduleWrite()\n return visitor\n }\n\n async updateVisitor(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n const idx = this.db.visitors.findIndex(v => v.visitorId === visitorId)\n if (idx === -1) throw new Error(`Visitor ${visitorId} not found`)\n const existing = this.db.visitors[idx]!\n const updated: Visitor = {\n ...existing,\n lastSeen: data.lastSeen ?? new Date().toISOString(),\n ...(data.country != null && { country: data.country }),\n ...(data.region != null && { region: data.region }),\n ...(data.city != null && { city: data.city }),\n }\n this.db.visitors[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteVisitor(visitorId: string): Promise<void> {\n this.db.visitors = this.db.visitors.filter(v => v.visitorId !== visitorId)\n this.scheduleWrite()\n }\n\n async getVisitor(visitorId: string): Promise<Visitor | null> {\n return this.db.visitors.find(v => v.visitorId === visitorId) ?? null\n }\n\n async getVisitors(filters: VisitorFilters): Promise<PagedResult<Visitor>> {\n let rows = this.db.visitors.filter(v => v.tenantId === filters.tenantId)\n if (filters.from) rows = rows.filter(v => v.firstSeen >= filters.from!)\n if (filters.to) rows = rows.filter(v => v.firstSeen <= filters.to!)\n if (filters.q) {\n const q = filters.q.toLowerCase()\n rows = rows.filter(v => v.visitorId.toLowerCase().startsWith(q) || (v.country?.toLowerCase().startsWith(q) ?? false))\n }\n rows.sort((a, b) => b.lastSeen.localeCompare(a.lastSeen))\n const page_ = filters.page ?? 1\n const limit = filters.limit ?? 50\n return { items: page(rows, page_, limit), total: rows.length, page: page_, limit }\n }\n\n // ── Users ──────────────────────────────────────────────────────────────────\n\n async createUser(data: CreateUserInput): Promise<AdminUser> {\n const now = new Date().toISOString()\n const user: AdminUser = {\n id: randomUUID(),\n tenantId: data.tenantId,\n name: data.name,\n email: data.email,\n passwordHash: data.passwordHash,\n isActive: true,\n allowedTenants: data.allowedTenants ?? [],\n createdAt: now,\n updatedAt: now,\n }\n this.db.users.push(user)\n this.scheduleWrite()\n return user\n }\n\n async updateUser(id: string, data: UpdateUserInput): Promise<AdminUser> {\n const idx = this.db.users.findIndex(u => u.id === id)\n if (idx === -1) throw new Error(`User ${id} not found`)\n const existing = this.db.users[idx]!\n const updated: AdminUser = {\n ...existing,\n ...(data.name != null && { name: data.name }),\n ...(data.email != null && { email: data.email }),\n ...(data.passwordHash != null && { passwordHash: data.passwordHash }),\n ...(data.isActive != null && { isActive: data.isActive }),\n ...('totpSecret' in data && data.totpSecret != null && { totpSecret: data.totpSecret }),\n ...(data.totpEnabled != null && { totpEnabled: data.totpEnabled }),\n ...('allowedTenants' in data && { allowedTenants: data.allowedTenants ?? [] }),\n updatedAt: new Date().toISOString(),\n }\n this.db.users[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteUser(id: string): Promise<void> {\n this.db.user_roles = this.db.user_roles.filter(ur => ur.userId !== id)\n this.db.users = this.db.users.filter(u => u.id !== id)\n this.scheduleWrite()\n }\n\n async getUserById(id: string): Promise<AdminUser | null> {\n return this.db.users.find(u => u.id === id) ?? null\n }\n\n async getUserByEmail(email: string): Promise<AdminUser | null> {\n return this.db.users.find(u => u.email === email) ?? null\n }\n\n async getUsers(tenantId: string): Promise<AdminUser[]> {\n return this.db.users.filter(u => u.tenantId === tenantId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n }\n\n async countUsers(tenantId?: string): Promise<number> {\n return tenantId\n ? this.db.users.filter(u => u.tenantId === tenantId).length\n : this.db.users.length\n }\n\n // ── Roles & Permissions ────────────────────────────────────────────────────\n\n async getRoles(tenantId: string): Promise<Role[]> {\n return this.db.roles.filter(r => r.tenantId === tenantId)\n }\n\n async createRole(data: CreateRoleInput): Promise<Role> {\n const role: Role = {\n id: randomUUID(),\n tenantId: data.tenantId,\n name: data.name,\n ...(data.description != null && { description: data.description }),\n }\n this.db.roles.push(role)\n this.scheduleWrite()\n return role\n }\n\n async updateRole(id: string, data: UpdateRoleInput): Promise<Role> {\n const idx = this.db.roles.findIndex(r => r.id === id)\n if (idx === -1) throw new Error(`Role ${id} not found`)\n const updated: Role = {\n ...this.db.roles[idx]!,\n ...(data.name != null && { name: data.name }),\n ...(data.description != null && { description: data.description }),\n }\n this.db.roles[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteRole(id: string): Promise<void> {\n this.db.role_permissions = this.db.role_permissions.filter(rp => rp.roleId !== id)\n this.db.user_roles = this.db.user_roles.filter(ur => ur.roleId !== id)\n this.db.roles = this.db.roles.filter(r => r.id !== id)\n this.scheduleWrite()\n }\n\n async getAllPermissions(): Promise<Permission[]> {\n return [...this.db.permissions].sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async getPermissionsForRole(roleId: string): Promise<Permission[]> {\n const permIds = new Set(\n this.db.role_permissions.filter(rp => rp.roleId === roleId).map(rp => rp.permissionId)\n )\n return this.db.permissions.filter(p => permIds.has(p.id))\n }\n\n async getUserRoles(userId: string, tenantId?: string): Promise<Role[]> {\n let urs = this.db.user_roles.filter(ur => ur.userId === userId)\n if (tenantId) urs = urs.filter(ur => ur.tenantId === tenantId)\n const roleIds = new Set(urs.map(ur => ur.roleId))\n return this.db.roles.filter(r => roleIds.has(r.id))\n }\n\n async assignRole(userId: string, roleId: string, tenantId = 'default'): Promise<void> {\n if (!this.db.user_roles.find(ur => ur.userId === userId && ur.roleId === roleId && ur.tenantId === tenantId)) {\n this.db.user_roles.push({ userId, roleId, tenantId })\n this.scheduleWrite()\n }\n }\n\n async revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> {\n this.db.user_roles = this.db.user_roles.filter(ur =>\n !(ur.userId === userId && ur.roleId === roleId && (tenantId == null || ur.tenantId === tenantId))\n )\n this.scheduleWrite()\n }\n\n async assignPermissionToRole(roleId: string, permissionId: string): Promise<void> {\n if (!this.db.role_permissions.find(rp => rp.roleId === roleId && rp.permissionId === permissionId)) {\n this.db.role_permissions.push({ roleId, permissionId })\n this.scheduleWrite()\n }\n }\n\n async revokePermissionFromRole(roleId: string, permissionId: string): Promise<void> {\n this.db.role_permissions = this.db.role_permissions.filter(\n rp => !(rp.roleId === roleId && rp.permissionId === permissionId)\n )\n this.scheduleWrite()\n }\n\n // ── Audit ──────────────────────────────────────────────────────────────────\n\n async createLog(data: CreateAuditLogInput): Promise<void> {\n const log: AuditLog = {\n id: randomUUID(),\n tenantId: data.tenantId,\n action: data.action,\n resourceType: data.resourceType,\n createdAt: new Date().toISOString(),\n ...(data.userId != null && { userId: data.userId }),\n ...(data.resourceId != null && { resourceId: data.resourceId }),\n ...(data.oldData != null && { oldData: data.oldData }),\n ...(data.newData != null && { newData: data.newData }),\n }\n this.db.audit_logs.push(log)\n this.scheduleWrite()\n }\n\n async getLogs(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n let rows = this.db.audit_logs.filter(l => l.tenantId === filters.tenantId)\n if (filters.action) rows = rows.filter(l => l.action === filters.action)\n if (filters.resourceType) rows = rows.filter(l => l.resourceType === filters.resourceType)\n if (filters.from) rows = rows.filter(l => l.createdAt >= filters.from!)\n if (filters.to) rows = rows.filter(l => l.createdAt <= filters.to!)\n if (filters.q) {\n const q = filters.q.toLowerCase()\n rows = rows.filter(l =>\n l.action.toLowerCase().startsWith(q) ||\n l.resourceType.toLowerCase().startsWith(q) ||\n (l.resourceId?.toLowerCase().startsWith(q) ?? false) ||\n (l.userId?.toLowerCase().startsWith(q) ?? false))\n }\n rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n const page_ = filters.page ?? 1\n const limit = filters.limit ?? 50\n return { items: page(rows, page_, limit).map(toAuditSummary), total: rows.length, page: page_, limit }\n }\n\n async getAuditLogById(id: string): Promise<AuditLog | null> {\n return this.db.audit_logs.find(l => l.id === id) ?? null\n }\n\n async *streamAuditLogs(filters: AuditFilters): AsyncIterable<AuditLog> {\n let rows = this.db.audit_logs.filter(l => l.tenantId === filters.tenantId)\n if (filters.action) rows = rows.filter(l => l.action === filters.action)\n if (filters.from) rows = rows.filter(l => l.createdAt >= filters.from!)\n if (filters.to) rows = rows.filter(l => l.createdAt <= filters.to!)\n rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n for (const log of rows) yield log\n }\n\n // ── Stats ──────────────────────────────────────────────────────────────────\n\n async getOverviewStats(tenantId: string): Promise<OverviewStats> {\n const consents = this.db.consents.filter(c => c.tenantId === tenantId)\n const recent = consents.slice(-500)\n let accepted = 0, rejected = 0\n for (const c of recent) {\n const vals = Object.values(c.consentJson)\n const grantedPct = vals.length > 0 ? vals.filter(v => v === 'granted').length / vals.length : 0\n if (grantedPct >= 0.8) accepted++\n else if (grantedPct <= 0.2) rejected++\n }\n const sample = recent.length || 1\n return {\n totalConsents: consents.length,\n gpcDetectedCount: consents.filter(c => c.gpcDetected).length,\n totalVisitors: this.db.visitors.filter(v => v.tenantId === tenantId).length,\n acceptedPct: Math.round((accepted / sample) * 100),\n rejectedPct: Math.round((rejected / sample) * 100),\n }\n }\n\n async getCategoryStats(tenantId: string): Promise<CategoryStats> {\n const stats: CategoryStats = {}\n for (const c of this.db.consents.filter(c => c.tenantId === tenantId).slice(0, 5000)) {\n for (const [key, status] of Object.entries(c.consentJson)) {\n if (!stats[key]) stats[key] = { granted: 0, denied: 0, objected: 0 }\n if (status === 'granted') stats[key]!.granted++\n else if (status === 'denied') stats[key]!.denied++\n else if (status === 'objected') stats[key]!.objected++\n }\n }\n return stats\n }\n\n async getTimeline(tenantId: string, days = 30): Promise<TimelineEntry[]> {\n const cutoff = new Date(Date.now() - days * 86_400_000).toISOString().slice(0, 10)\n const counts = new Map<string, number>()\n for (const c of this.db.consents.filter(c => c.tenantId === tenantId && c.createdAt.slice(0, 10) >= cutoff)) {\n const date = c.createdAt.slice(0, 10)\n counts.set(date, (counts.get(date) ?? 0) + 1)\n }\n return [...counts.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, count]) => ({ date, count }))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n const consentVisitors = new Set(\n this.db.consents.filter(c => c.tenantId === tenantId).map(c => c.visitorId)\n )\n const counts = new Map<string, number>()\n for (const v of this.db.visitors.filter(v => consentVisitors.has(v.visitorId) && v.country)) {\n const c = v.country!\n counts.set(c, (counts.get(c) ?? 0) + 1)\n }\n return [...counts.entries()]\n .sort(([, a], [, b]) => b - a)\n .slice(0, 50)\n .map(([country, count]) => ({ country, count }))\n }\n\n async getGpcStats(tenantId: string): Promise<GpcStats> {\n const consents = this.db.consents.filter(c => c.tenantId === tenantId)\n const detected = consents.filter(c => c.gpcDetected).length\n const total = consents.length\n return { detected, total, rate: total > 0 ? Math.round((detected / total) * 100) : 0 }\n }\n\n // ── Tenants ────────────────────────────────────────────────────────────────\n\n async getTenants(): Promise<Tenant[]> {\n return [...this.db.tenants].sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async createTenant(data: CreateTenantInput): Promise<Tenant> {\n const now = new Date().toISOString()\n const tenant: Tenant = { id: randomUUID(), name: data.name, slug: data.slug, createdAt: now, updatedAt: now }\n this.db.tenants.push(tenant)\n this.scheduleWrite()\n return tenant\n }\n\n async updateTenant(id: string, data: UpdateTenantInput): Promise<Tenant> {\n const idx = this.db.tenants.findIndex(t => t.id === id)\n if (idx === -1) throw new Error(`Tenant ${id} not found`)\n const now = new Date().toISOString()\n const updated: Tenant = {\n ...this.db.tenants[idx]!,\n ...(data.name != null && { name: data.name }),\n ...(data.slug != null && { slug: data.slug }),\n updatedAt: now,\n }\n this.db.tenants[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteTenant(id: string): Promise<void> {\n this.db.tenants = this.db.tenants.filter(t => t.id !== id)\n this.scheduleWrite()\n }\n\n async getSettings(tenantId: string): Promise<TenantSettings> {\n return this.db.settings[tenantId] ?? {}\n }\n\n async updateSettings(tenantId: string, data: Partial<TenantSettings>): Promise<TenantSettings> {\n const updated = { ...(this.db.settings[tenantId] ?? {}), ...data }\n this.db.settings[tenantId] = updated\n this.scheduleWrite()\n return updated\n }\n\n async purgeExpiredConsents(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const toDelete = new Set(\n this.db.consents.filter(c => c.createdAt < cutoff).map(c => c.visitorId)\n )\n if (toDelete.size === 0) return 0\n this.db.consent_history = this.db.consent_history.filter(h => !toDelete.has(h.visitorId))\n this.db.consents = this.db.consents.filter(c => !toDelete.has(c.visitorId))\n this.scheduleWrite()\n return toDelete.size\n }\n\n async purgeExpiredAuditLogs(olderThanDays: number): Promise<number> {\n const cutoff = new Date(Date.now() - olderThanDays * 86_400_000).toISOString()\n const before = this.db.audit_logs.length\n this.db.audit_logs = this.db.audit_logs.filter(l => l.createdAt >= cutoff)\n const removed = before - this.db.audit_logs.length\n if (removed > 0) this.scheduleWrite()\n return removed\n }\n\n // ── API Keys ───────────────────────────────────────────────────────────────\n\n async createApiKey(data: CreateApiKeyInput): Promise<ApiKey> {\n const now = new Date().toISOString()\n const key: ApiKey = {\n id: randomUUID(),\n tenantId: data.tenantId,\n keyHash: data.keyHash,\n name: data.name,\n isActive: true,\n ...(data.createdBy !== undefined ? { createdBy: data.createdBy } : {}),\n ...(data.expireBy !== undefined ? { expireBy: data.expireBy } : {}),\n createdAt: now,\n updatedAt: now,\n }\n this.db.api_keys.push(key)\n this.scheduleWrite()\n return key\n }\n\n async getApiKeyByHash(keyHash: string): Promise<ApiKey | null> {\n return this.db.api_keys.find(k => k.keyHash === keyHash && k.isActive) ?? null\n }\n\n async revokeApiKey(id: string): Promise<void> {\n const key = this.db.api_keys.find(k => k.id === id)\n if (key) { key.isActive = false; key.updatedAt = new Date().toISOString(); this.scheduleWrite() }\n }\n\n async reactivateApiKey(id: string): Promise<void> {\n const key = this.db.api_keys.find(k => k.id === id)\n if (!key) return\n key.isActive = true\n if (key.expireBy && new Date(key.expireBy) <= new Date()) delete key.expireBy\n key.updatedAt = new Date().toISOString()\n this.scheduleWrite()\n }\n\n async deleteApiKey(id: string): Promise<void> {\n const idx = this.db.api_keys.findIndex(k => k.id === id)\n if (idx === -1) return\n this.db.api_keys.splice(idx, 1)\n this.scheduleWrite()\n }\n\n async getApiKeys(tenantId: string): Promise<ApiKey[]> {\n return this.db.api_keys\n .filter(k => k.tenantId === tenantId)\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n }\n\n // ── Consent Templates ──────────────────────────────────────────────────────\n\n async createConsentTemplate(data: CreateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const now = new Date().toISOString()\n const tpl: ServerConsentTemplate = {\n id: randomConsentTemplateId(), tenantId: data.tenantId, name: data.name,\n cookies: data.cookies, categories: data.categories, createdAt: now, updatedAt: now,\n }\n this.db.consent_templates.push(tpl)\n this.scheduleWrite()\n return tpl\n }\n\n async updateConsentTemplate(id: string, data: UpdateConsentTemplateInput): Promise<ServerConsentTemplate> {\n const idx = this.db.consent_templates.findIndex(t => t.id === id)\n if (idx === -1) throw new Error('Consent template not found')\n const now = new Date().toISOString()\n const updated: ServerConsentTemplate = {\n ...this.db.consent_templates[idx]!,\n ...(data.name !== undefined && { name: data.name }),\n ...(data.cookies !== undefined && { cookies: data.cookies }),\n ...(data.categories !== undefined && { categories: data.categories }),\n updatedAt: now,\n }\n this.db.consent_templates[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteConsentTemplate(id: string): Promise<void> {\n this.db.consent_templates = this.db.consent_templates.filter(t => t.id !== id)\n this.scheduleWrite()\n }\n\n async getConsentTemplate(id: string): Promise<ServerConsentTemplate | null> {\n return this.db.consent_templates.find(t => t.id === id) ?? null\n }\n\n async getConsentTemplates(tenantId: string): Promise<ServerConsentTemplate[]> {\n return this.db.consent_templates\n .filter(t => t.tenantId === tenantId)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async copyConsentTemplate(id: string, newName: string): Promise<ServerConsentTemplate> {\n const src = await this.getConsentTemplate(id)\n if (!src) throw new Error('Consent template not found')\n return this.createConsentTemplate({ tenantId: src.tenantId, name: newName, cookies: src.cookies, categories: src.categories })\n }\n\n // ── UI Templates ───────────────────────────────────────────────────────────\n\n async createUITemplate(data: CreateUITemplateInput): Promise<ServerUITemplate> {\n const now = new Date().toISOString()\n const { tenantId, name, ...settings } = data\n const tpl = { id: randomUITemplateId(), tenantId, name, ...settings, createdAt: now, updatedAt: now } as ServerUITemplate\n this.db.ui_templates.push(tpl)\n this.scheduleWrite()\n return tpl\n }\n\n async updateUITemplate(id: string, data: UpdateUITemplateInput): Promise<ServerUITemplate> {\n const idx = this.db.ui_templates.findIndex(t => t.id === id)\n if (idx === -1) throw new Error('UI template not found')\n const { name: _name, ...settings } = data\n const now = new Date().toISOString()\n const updated = {\n ...this.db.ui_templates[idx]!,\n ...(data.name !== undefined && { name: data.name }),\n ...settings,\n updatedAt: now,\n } as ServerUITemplate\n this.db.ui_templates[idx] = updated\n this.scheduleWrite()\n return updated\n }\n\n async deleteUITemplate(id: string): Promise<void> {\n this.db.ui_templates = this.db.ui_templates.filter(t => t.id !== id)\n this.scheduleWrite()\n }\n\n async getUITemplate(id: string): Promise<ServerUITemplate | null> {\n return this.db.ui_templates.find(t => t.id === id) ?? null\n }\n\n async getUITemplates(tenantId: string): Promise<ServerUITemplate[]> {\n return this.db.ui_templates\n .filter(t => t.tenantId === tenantId)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n async copyUITemplate(id: string, newName: string): Promise<ServerUITemplate> {\n const src = await this.getUITemplate(id)\n if (!src) throw new Error('UI template not found')\n const { tenantId, mainBanner, gpcBanner, preferenceModal } = src as ServerUITemplate & {\n mainBanner?: unknown; gpcBanner?: unknown; preferenceModal?: unknown\n }\n return this.createUITemplate({\n tenantId, name: newName,\n ...(mainBanner !== undefined && { mainBanner }),\n ...(gpcBanner !== undefined && { gpcBanner }),\n ...(preferenceModal !== undefined && { preferenceModal }),\n } as CreateUITemplateInput)\n }\n\n // ── Profile summaries ──────────────────────────────────────────────────────\n\n private profileToSummary(p: Profile): ProfileSummary {\n const json = p.profileJson as StoredProfileJson & { complianceGroup?: string; customComplianceGroup?: string; isActive?: boolean; consentTemplateId?: string; uiTemplateId?: string }\n const ct = json.consentTemplateId ? this.db.consent_templates.find(t => t.id === json.consentTemplateId) : null\n const ut = json.uiTemplateId ? this.db.ui_templates.find(t => t.id === json.uiTemplateId) : null\n return {\n id: p.id,\n name: p.name,\n defaultLocale: p.defaultLocale,\n complianceGroup: (json.complianceGroup ?? null) as ComplianceGroupId | null,\n customComplianceGroup: json.customComplianceGroup ?? null,\n isActive: json.isActive ?? false,\n consentTemplateName: ct?.name ?? null,\n uiTemplateName: ut?.name ?? null,\n createdAt: p.createdAt,\n updatedAt: p.updatedAt,\n }\n }\n\n async listProfilesSummary(tenantId: string): Promise<ProfileSummary[]> {\n return this.db.profiles\n .filter(p => p.tenantId === tenantId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n .map(p => this.profileToSummary(p))\n }\n\n async findProfilesUsingConsentTemplate(templateId: string): Promise<ProfileSummary[]> {\n return this.db.profiles\n .filter(p => {\n const json = p.profileJson as { consentTemplateId?: string }\n return json.consentTemplateId === templateId\n })\n .sort((a, b) => a.name.localeCompare(b.name))\n .map(p => this.profileToSummary(p))\n }\n\n async findProfilesUsingUITemplate(templateId: string): Promise<ProfileSummary[]> {\n return this.db.profiles\n .filter(p => {\n const json = p.profileJson as { uiTemplateId?: string }\n return json.uiTemplateId === templateId\n })\n .sort((a, b) => a.name.localeCompare(b.name))\n .map(p => this.profileToSummary(p))\n }\n\n // ── Analytics ──────────────────────────────────────────────────────────────\n\n async getOptInStats(tenantId: string, filters: OptInFilters): Promise<OptInStats> {\n let records = this.db.consents.filter(c => c.tenantId === tenantId)\n if (filters.profileId) records = records.filter(c => c.profileId === filters.profileId)\n if (filters.from) records = records.filter(c => c.createdAt >= filters.from!)\n if (filters.to) records = records.filter(c => c.createdAt <= filters.to!)\n if (filters.locale) records = records.filter(c => c.locale === filters.locale)\n\n let total = 0, granted = 0, denied = 0, managed = 0\n const byLocale: Record<string, { total: number; granted: number; denied: number; managed: number }> = {}\n const dateMap = new Map<string, { total: number; granted: number; denied: number; managed: number }>()\n\n for (const r of records) {\n const statuses = Object.values(r.consentJson as Record<string, string>)\n const grantedPct = statuses.length > 0 ? statuses.filter(s => s === 'granted').length / statuses.length : 0\n const deniedPct = statuses.length > 0 ? statuses.filter(s => s === 'denied').length / statuses.length : 0\n total++\n const loc = r.locale\n if (!byLocale[loc]) byLocale[loc] = { total: 0, granted: 0, denied: 0, managed: 0 }\n byLocale[loc]!.total++\n\n const date = r.createdAt.slice(0, 10)\n const entry = dateMap.get(date) ?? { total: 0, granted: 0, denied: 0, managed: 0 }\n entry.total++\n\n if (grantedPct >= 0.9) { granted++; byLocale[loc]!.granted++; entry.granted++ }\n else if (deniedPct >= 0.9) { denied++; byLocale[loc]!.denied++; entry.denied++ }\n else { managed++; byLocale[loc]!.managed++; entry.managed++ }\n dateMap.set(date, entry)\n }\n\n const byDate = Array.from(dateMap.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, v]) => ({ date, ...v }))\n\n return {\n total, granted, denied, managed,\n grantedPct: total > 0 ? Math.round((granted / total) * 1000) / 10 : 0,\n deniedPct: total > 0 ? Math.round((denied / total) * 1000) / 10 : 0,\n managedPct: total > 0 ? Math.round((managed / total) * 1000) / 10 : 0,\n byLocale,\n byDate,\n }\n }\n}\n","import type {\n StorageAdapter, Profile, CreateProfileInput, UpdateProfileInput,\n} from '@consenti/types'\n\nexport class ProfileRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateProfileInput): Promise<Profile> {\n return this.adapter.createProfile(data)\n }\n\n update(id: string, data: UpdateProfileInput): Promise<Profile> {\n return this.adapter.updateProfile(id, data)\n }\n\n delete(id: string): Promise<void> {\n return this.adapter.deleteProfile(id)\n }\n\n get(id: string): Promise<Profile | null> {\n return this.adapter.getProfile(id)\n }\n\n list(tenantId: string): Promise<Profile[]> {\n return this.adapter.getProfiles(tenantId)\n }\n\n findActiveByComplianceGroup(tenantId: string, complianceGroup: string): Promise<Profile | null> {\n return this.adapter.findActiveProfileByComplianceGroup(tenantId, complianceGroup)\n }\n}\n","import type {\n StorageAdapter, ConsentDbRecord, ConsentSummary, CreateConsentInput, UpdateConsentInput,\n ConsentFilters, PagedResult,\n} from '@consenti/types'\n\nexport class ConsentRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateConsentInput): Promise<ConsentDbRecord> {\n return this.adapter.createConsent(data)\n }\n\n update(visitorId: string, data: UpdateConsentInput): Promise<ConsentDbRecord> {\n return this.adapter.updateConsent(visitorId, data)\n }\n\n delete(visitorId: string): Promise<void> {\n return this.adapter.deleteConsent(visitorId)\n }\n\n get(visitorId: string): Promise<ConsentDbRecord | null> {\n return this.adapter.getConsent(visitorId)\n }\n\n list(filters: ConsentFilters): Promise<PagedResult<ConsentSummary>> {\n return this.adapter.getConsents(filters)\n }\n}\n","import type {\n StorageAdapter, Visitor, CreateVisitorInput, UpdateVisitorInput,\n} from '@consenti/types'\n\nexport class VisitorRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateVisitorInput): Promise<Visitor> {\n return this.adapter.createVisitor(data)\n }\n\n update(visitorId: string, data: UpdateVisitorInput): Promise<Visitor> {\n return this.adapter.updateVisitor(visitorId, data)\n }\n\n delete(visitorId: string): Promise<void> {\n return this.adapter.deleteVisitor(visitorId)\n }\n\n get(visitorId: string): Promise<Visitor | null> {\n return this.adapter.getVisitor(visitorId)\n }\n}\n","import type {\n StorageAdapter, NoticeShownRecord, CreateNoticeShownInput,\n} from '@consenti/types'\n\nexport class NoticeRepo {\n constructor(private adapter: StorageAdapter) {}\n\n create(data: CreateNoticeShownInput): Promise<NoticeShownRecord> {\n return this.adapter.createNoticeShown(data)\n }\n\n getForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n return this.adapter.getNoticeShownForVisitor(visitorId)\n }\n}\n","import type {\n StorageAdapter, AuditLog, AuditLogSummary, AuditFilters, CreateAuditLogInput, PagedResult,\n} from '@consenti/types'\n\nexport class AuditRepo {\n constructor(private adapter: StorageAdapter) {}\n\n log(data: CreateAuditLogInput): Promise<void> {\n return this.adapter.createLog(data)\n }\n\n list(filters: AuditFilters): Promise<PagedResult<AuditLogSummary>> {\n return this.adapter.getLogs(filters)\n }\n\n get(id: string): Promise<AuditLog | null> {\n return this.adapter.getAuditLogById(id)\n }\n}\n","interface CacheEntry<T> { value: T; expiresAt: number }\n\n/**\n * Small in-memory TTL cache with a hard size bound and periodic sweeping — used to front\n * expensive, rarely-changing reads (report/stats queries) without needing an external cache\n * (this project ships zero runtime dependencies). Bounded so a multi-tenant deployment with many\n * tenants/query combinations can't grow the cache without limit between sweeps; swept on an\n * interval so expired entries are reclaimed even if nothing happens to re-touch that key.\n */\nexport class TTLCache {\n private store = new Map<string, CacheEntry<unknown>>()\n private sweepTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(\n private ttlMs: number,\n private maxEntries = 500,\n sweepIntervalMs = 60_000,\n ) {\n this.sweepTimer = setInterval(() => this.sweep(), sweepIntervalMs)\n this.sweepTimer.unref?.()\n }\n\n get<T>(key: string): T | undefined {\n const entry = this.store.get(key)\n if (!entry) return undefined\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key)\n return undefined\n }\n return entry.value as T\n }\n\n set<T>(key: string, value: T): void {\n if (this.store.size >= this.maxEntries && !this.store.has(key)) {\n // Map preserves insertion order — the first key is the oldest-inserted entry, a\n // reasonable-enough eviction target without tracking separate access-time metadata.\n const oldest = this.store.keys().next().value\n if (oldest !== undefined) this.store.delete(oldest)\n }\n this.store.set(key, { value, expiresAt: Date.now() + this.ttlMs })\n }\n\n private sweep(): void {\n const now = Date.now()\n for (const [key, entry] of this.store) {\n if (now > entry.expiresAt) this.store.delete(key)\n }\n }\n\n /** Stops the sweep interval — call on shutdown so it doesn't keep the process alive. */\n dispose(): void {\n if (this.sweepTimer) clearInterval(this.sweepTimer)\n this.sweepTimer = null\n }\n}\n","import type {\n StorageAdapter, OverviewStats, CategoryStats, TimelineEntry, CountryStat, GpcStats, Tenant,\n} from '@consenti/types'\nimport { TTLCache } from '../utils/ttl-cache'\n\n// Stats/report queries scan the whole tenant's consent history — expensive, and rarely need\n// per-second freshness for a reporting page. 30s keeps the dashboard feeling live (a fresh\n// consent shows up within half a minute) while absorbing repeat page views/refreshes for free.\nconst STATS_CACHE_TTL_MS = 30_000\n\nexport class StatsService {\n private cache = new TTLCache(STATS_CACHE_TTL_MS)\n\n constructor(private storage: StorageAdapter) {}\n\n async getOverview(tenantId: string): Promise<OverviewStats> {\n return this.cached(`overview:${tenantId}`, () => this.storage.getOverviewStats(tenantId))\n }\n\n async getCategories(tenantId: string): Promise<CategoryStats> {\n return this.cached(`categories:${tenantId}`, () => this.storage.getCategoryStats(tenantId))\n }\n\n async getTimeline(tenantId: string, days: number): Promise<TimelineEntry[]> {\n return this.cached(`timeline:${tenantId}:${days}`, () => this.storage.getTimeline(tenantId, days))\n }\n\n async getCountries(tenantId: string): Promise<CountryStat[]> {\n return this.cached(`countries:${tenantId}`, () => this.storage.getCountries(tenantId))\n }\n\n async getGpc(tenantId: string): Promise<GpcStats> {\n return this.cached(`gpc:${tenantId}`, () => this.storage.getGpcStats(tenantId))\n }\n\n async getTenants(): Promise<Tenant[]> {\n return this.storage.getTenants()\n }\n\n dispose(): void {\n this.cache.dispose()\n }\n\n private async cached<T>(key: string, load: () => Promise<T>): Promise<T> {\n const hit = this.cache.get<T>(key)\n if (hit !== undefined) return hit\n const value = await load()\n this.cache.set(key, value)\n return value\n }\n}\n","import type {\n StorageAdapter,\n AdminUser,\n CreateUserInput,\n UpdateUserInput,\n Role,\n} from '@consenti/types'\n\nexport class UserRepo {\n constructor(private adapter: StorageAdapter) {}\n\n getById(id: string): Promise<AdminUser | null> { return this.adapter.getUserById(id) }\n getByEmail(email: string): Promise<AdminUser | null> { return this.adapter.getUserByEmail(email) }\n list(tenantId: string): Promise<AdminUser[]> { return this.adapter.getUsers(tenantId) }\n create(data: CreateUserInput): Promise<AdminUser> { return this.adapter.createUser(data) }\n update(id: string, data: UpdateUserInput): Promise<AdminUser> { return this.adapter.updateUser(id, data) }\n delete(id: string): Promise<void> { return this.adapter.deleteUser(id) }\n getRoles(userId: string, tenantId?: string): Promise<Role[]> { return this.adapter.getUserRoles(userId, tenantId) }\n assignRole(userId: string, roleId: string, tenantId?: string): Promise<void> { return this.adapter.assignRole(userId, roleId, tenantId) }\n revokeRole(userId: string, roleId: string, tenantId?: string): Promise<void> { return this.adapter.revokeRole(userId, roleId, tenantId) }\n}\n","import type { Profile, CreateProfileInput, UpdateProfileInput, PublicProfileResponse, ProfileSummary, ProfileVersionEntry, ArchivedProfileSummary, StorageAdapter, CookieMap, S3ApiConfig, MainBanner, GpcBanner, PreferenceModal, LocaleContentInput, StoredProfileJson } from '@consenti/types'\nimport type { ProfileRepo } from '../repositories/profile.repo'\nimport type { AuditRepo } from '../repositories/audit.repo'\nimport type { EventEmitter } from 'node:events'\nimport type { LocaleJsonCacheService } from './locale-json-cache.service'\nimport { readdirSync, readFileSync, existsSync, statSync, lstatSync } from 'node:fs'\nimport type { Dirent } from 'node:fs'\nimport { join } from 'node:path'\nimport { runProfileJsonWrite } from '../workers/profile-json-worker-launcher.js'\nimport type { WorkerMessage } from '../workers/profile-json-worker-core.js'\nimport { randomUUID } from '../utils/crypto'\n\n/**\n * Merges a profile's `cookiesOverride` deltas onto the template-resolved `CookieMap`. Deltas for\n * a cookie id not present in `cookies` are ignored — overrides tune an existing template-authored\n * parameter, they don't add new ones. `categoriesOverride`/`uiOverride` have no equivalent yet —\n * they're stored on `ProfileConfig` but not applied here (reserved for a future phase).\n */\nexport function applyCookiesOverride(cookies: CookieMap, override?: Record<string, Partial<CookieMap[string]>>): CookieMap {\n if (!override) return cookies\n const result: CookieMap = { ...cookies }\n for (const [id, delta] of Object.entries(override)) {\n if (!result[id]) continue\n result[id] = { ...result[id], ...delta }\n }\n return result\n}\n\n/** One locale's resolved banner/modal content — the unit both `StoredProfileJson`'s own\n * `mainBanner`/`gpcBanner`/`preferenceModal` (default locale) and each non-default locale's\n * on-disk version file store. */\ninterface ResolvedLocaleContent {\n mainBanner: MainBanner\n gpcBanner?: GpcBanner\n preferenceModal: PreferenceModal\n}\n\n/**\n * Profile-wide settings (not locale-specific) that belong on every resolved response and every\n * on-disk locale file — `getResolved()`'s DB-fallback path and `writeLocaleFiles()`'s worker\n * input both need the exact same set, so this is the one place they're read off `pj`. Previously\n * only `cookies`/`expiryDays` made this trip; `gpcMode`/`complianceGroup`/`darkMode`/`allowReceipt`/\n * `enhanceAccessibility`/`showFooterMetadata`/`allowedOrigins`/`complianceConfig`/`dpdpa`/\n * `hidePoweredBy` were silently dropped, so a dashboard-configured toggle never reached a real\n * visitor's widget even on an active, API-backed profile.\n */\nfunction profileWideFields(pj: StoredProfileJson): Pick<PublicProfileResponse,\n 'gpcMode' | 'complianceGroup' | 'darkMode' | 'allowReceipt' | 'enhanceAccessibility' |\n 'showFooterMetadata' | 'allowedOrigins' | 'complianceConfig' | 'dpdpa' | 'hidePoweredBy'\n> {\n return {\n ...(pj.gpcMode !== undefined ? { gpcMode: pj.gpcMode } : {}),\n ...(pj.complianceGroup !== undefined ? { complianceGroup: pj.complianceGroup } : {}),\n ...(pj.darkMode !== undefined ? { darkMode: pj.darkMode } : {}),\n ...(pj.allowReceipt !== undefined ? { allowReceipt: pj.allowReceipt } : {}),\n ...(pj.enhanceAccessibility !== undefined ? { enhanceAccessibility: pj.enhanceAccessibility } : {}),\n ...(pj.showFooterMetadata !== undefined ? { showFooterMetadata: pj.showFooterMetadata } : {}),\n ...(pj.allowedOrigins !== undefined ? { allowedOrigins: pj.allowedOrigins } : {}),\n ...(pj.complianceConfig !== undefined ? { complianceConfig: pj.complianceConfig } : {}),\n ...(pj.dpdpa !== undefined ? { dpdpa: pj.dpdpa } : {}),\n ...(pj.hidePoweredBy !== undefined ? { hidePoweredBy: pj.hidePoweredBy } : {}),\n }\n}\n\nexport class ProfileService {\n constructor(\n private profiles: ProfileRepo,\n private audit: AuditRepo,\n private tenantId: string = 'default',\n private storage?: StorageAdapter,\n private eventBus?: EventEmitter,\n private localeCache?: LocaleJsonCacheService,\n private profilesDir?: string,\n private handleCache?: (paths: string[], profileId: string, isPurge: boolean) => void,\n private s3Config?: S3ApiConfig,\n ) { }\n\n /** Root directory holding every version snapshot of one logical profile, keyed by its stable id. */\n private versionsRootDir(profileId: string): string {\n return join(this.profilesDir ?? '', this.tenantId, profileId)\n }\n\n private complianceGroupDir(complianceGroup: string): string {\n return join(this.profilesDir ?? '', this.tenantId, complianceGroup)\n }\n\n /**\n * The directory key used for activation/hot-serve writes and the \"one active profile\n * per group\" conflict check — the real `complianceGroup` when set, otherwise the\n * free-form `customComplianceGroup` (which the widget's `compliance.type` targets the\n * same way). Compliance *validation* still only ever runs against a real `complianceGroup`.\n */\n private groupKey(pj: { complianceGroup?: string; customComplianceGroup?: string }): string {\n return pj.complianceGroup || pj.customComplianceGroup || ''\n }\n\n private profileFilePaths(complianceGroup: string, locales: string[]): string[] {\n const dir = this.complianceGroupDir(complianceGroup)\n return locales.map(l => join(dir, `${l}.json`)).concat(join(dir, 'default.json'))\n }\n\n /** Resolves this profile's cookie map (consent-template lookup + `cookiesOverride` applied) —\n * the one piece of content still resolved live rather than baked at save time, since cookies\n * are small and never the source of the row-size problem `mainBanner`/`preferenceModal` were. */\n private async resolveCookies(pj: { cookies?: CookieMap; consentTemplateId?: string; cookiesOverride?: Record<string, Partial<CookieMap[string]>> }): Promise<CookieMap> {\n let cookies: CookieMap = pj.cookies ?? {}\n if (pj.consentTemplateId && this.storage) {\n const ct = await this.storage.getConsentTemplate(pj.consentTemplateId)\n if (ct) cookies = ct.cookies\n }\n return applyCookiesOverride(cookies, pj.cookiesOverride)\n }\n\n /**\n * Writes every locale's on-disk version file for one save (create or an edit) in a single\n * atomic worker call — default locale content comes from `profile.profileJson` itself, every\n * other touched locale from `localeContent`. A locale already in `profileJson.locales` but not\n * present in `localeContent` (a tab the user never opened this session) is carried forward\n * unchanged from `previousVersion` by the worker — never silently dropped.\n */\n private async writeLocaleFiles(\n profile: Profile,\n localeContent: Record<string, LocaleContentInput> | undefined,\n complianceGroup: string,\n previousVersion: number | undefined,\n ): Promise<void> {\n if (!this.profilesDir) return\n const pj = profile.profileJson\n const cookies = await this.resolveCookies(pj)\n const locales = pj.locales?.length ? pj.locales : [profile.defaultLocale]\n\n const allLocaleContent: Record<string, ResolvedLocaleContent> = {\n [profile.defaultLocale]: {\n mainBanner: pj.mainBanner,\n ...(pj.gpcBanner ? { gpcBanner: pj.gpcBanner } : {}),\n preferenceModal: pj.preferenceModal,\n },\n ...(localeContent ?? {}),\n }\n\n const isActive = pj.isActive ?? false\n const action: WorkerMessage['action'] = isActive ? 'activate' : 'write'\n const msg: WorkerMessage = {\n action,\n storagePath: join(this.profilesDir, '..'),\n tenantId: this.tenantId,\n profileId: profile.id,\n version: profile.version,\n complianceGroup,\n profile: {\n id: profile.id,\n defaultLocale: profile.defaultLocale,\n locales,\n cookies,\n ...(pj.expiryDays !== undefined ? { expiryDays: pj.expiryDays } : {}),\n ...profileWideFields(pj),\n },\n localeContent: allLocaleContent,\n ...(previousVersion !== undefined ? { previousVersion } : {}),\n ...(this.s3Config?.enabled ? { s3Config: this.s3Config } : {}),\n }\n await runProfileJsonWrite(msg)\n\n if (isActive) {\n const paths = this.profileFilePaths(complianceGroup, locales)\n this.handleCache?.(paths, profile.id, true)\n this.eventBus?.emit('cache:purge', { paths, profileId: profile.id })\n }\n }\n\n async create(input: Omit<CreateProfileInput, 'tenantId'>): Promise<Profile> {\n const { localeContent, ...rest } = input\n const profile = await this.profiles.create({ ...rest, tenantId: this.tenantId })\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'profile.created',\n resourceType: 'profile',\n resourceId: profile.id,\n newData: { profileId: profile.id, version: profile.version, complianceGroup: this.groupKey(profile.profileJson) },\n })\n this.eventBus?.emit('profile.created', profile)\n\n if (this.profilesDir) {\n const complianceGroup = this.groupKey(profile.profileJson)\n await this.writeLocaleFiles(profile, localeContent, complianceGroup, undefined)\n const locales = profile.profileJson.locales?.length ? profile.profileJson.locales : [profile.defaultLocale]\n const paths = this.profileFilePaths(complianceGroup, locales)\n this.handleCache?.(paths, profile.id, false)\n this.eventBus?.emit('cache:warm', { paths, profileId: profile.id })\n }\n return profile\n }\n\n /**\n * Duplicates a profile as a brand new, always-inactive profile (own `id`, `version` starts at\n * 1 again). Delegates to {@link create} rather than copying on-disk files directly — the\n * resolved-JSON snapshot embeds its own `id`/`version`, so it has to be regenerated for the\n * new profile rather than byte-copied from the source's. Non-default locale content is read\n * back off the source's current version files so the copy carries every locale forward too.\n */\n async copy(id: string, name?: string): Promise<Profile> {\n const src = await this.profiles.get(id)\n if (!src) throw new Error(`Profile ${id} not found`)\n const locales = src.profileJson.locales?.length ? src.profileJson.locales : [src.defaultLocale]\n const localeContent: Record<string, LocaleContentInput> = {}\n for (const locale of locales) {\n if (locale === src.defaultLocale) continue\n const raw = await this.getVersionFile(id, String(src.version), locale)\n if (!raw) continue\n try {\n const parsed = JSON.parse(raw) as ResolvedLocaleContent\n localeContent[locale] = {\n mainBanner: parsed.mainBanner,\n ...(parsed.gpcBanner ? { gpcBanner: parsed.gpcBanner } : {}),\n preferenceModal: parsed.preferenceModal,\n }\n } catch { /* skip an unreadable/corrupt locale file — copy proceeds without it */ }\n }\n return this.create({\n name: name ?? `Copy of ${src.name}`,\n defaultLocale: src.defaultLocale,\n profileJson: { ...src.profileJson, isActive: false },\n ...(Object.keys(localeContent).length > 0 ? { localeContent } : {}),\n })\n }\n\n /**\n * Mutates the existing row in place — `id` is stable across every edit, `version` is\n * incremented. The prior state isn't kept as a separate DB row; its resolved-JSON snapshot\n * on disk (see {@link listVersions}) is the audit trail for that edit.\n */\n async update(id: string, input: UpdateProfileInput): Promise<Profile> {\n const old = await this.profiles.get(id)\n if (!old) throw new Error(`Profile ${id} not found`)\n const { localeContent, ...rest } = input\n\n // Activation is a dedicated action (activate()/deactivate()), never something the\n // profile editor form sends — carry the old row's isActive forward unless the\n // caller explicitly set it, so editing an active profile doesn't silently deactivate it.\n const carriedIsActive = rest.profileJson?.isActive ?? old.profileJson.isActive\n const profileJson = rest.profileJson\n ? { ...rest.profileJson, ...(carriedIsActive !== undefined ? { isActive: carriedIsActive } : {}) }\n : old.profileJson\n\n const profile = await this.profiles.update(id, {\n name: rest.name ?? old.name,\n defaultLocale: rest.defaultLocale ?? old.defaultLocale,\n profileJson,\n version: old.version + 1,\n })\n this.localeCache?.invalidate(id)\n\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'profile.updated',\n resourceType: 'profile',\n resourceId: profile.id,\n oldData: { profileId: old.id, version: old.version },\n newData: { profileId: profile.id, version: profile.version, complianceGroup: this.groupKey(profile.profileJson) },\n })\n this.eventBus?.emit('profile.updated', { previous: old, current: profile })\n\n if (this.profilesDir) {\n const complianceGroup = this.groupKey(profile.profileJson)\n await this.writeLocaleFiles(profile, localeContent, complianceGroup, old.version)\n }\n return profile\n }\n\n async delete(id: string): Promise<void> {\n const old = await this.profiles.get(id)\n await this.profiles.delete(id)\n this.localeCache?.invalidate(id)\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'profile.deleted',\n resourceType: 'profile',\n resourceId: id,\n ...(old != null ? { oldData: { profileId: old.id, version: old.version } } : {}),\n })\n this.eventBus?.emit('profile.deleted', { id, previous: old })\n\n if (this.profilesDir && old) {\n const complianceGroup = this.groupKey(old.profileJson)\n const wasActive = old.profileJson.isActive ?? false\n if (wasActive && complianceGroup) {\n const locales = old.profileJson.locales?.length ? old.profileJson.locales : [old.defaultLocale]\n await this.runDeactivate(complianceGroup)\n const paths = this.profileFilePaths(complianceGroup, locales)\n this.handleCache?.(paths, id, true)\n this.eventBus?.emit('cache:purge', { paths, profileId: id })\n }\n }\n }\n\n async activate(id: string): Promise<Profile> {\n const profile = await this.profiles.get(id)\n if (!profile) throw new Error(`Profile ${id} not found`)\n const groupKey = this.groupKey(profile.profileJson)\n const updated = await this.profiles.update(id, {\n profileJson: { ...profile.profileJson, isActive: true },\n })\n this.localeCache?.invalidate(id)\n\n if (this.profilesDir && groupKey) {\n await this.writeLocaleFiles(updated, undefined, groupKey, profile.version)\n }\n await this.audit.log({ tenantId: this.tenantId, action: 'profile.activated', resourceType: 'profile', resourceId: id })\n this.eventBus?.emit('profile.activated', updated)\n return updated\n }\n\n async deactivate(id: string): Promise<Profile> {\n const profile = await this.profiles.get(id)\n if (!profile) throw new Error(`Profile ${id} not found`)\n const groupKey = this.groupKey(profile.profileJson)\n const updated = await this.profiles.update(id, {\n profileJson: { ...profile.profileJson, isActive: false },\n })\n this.localeCache?.invalidate(id)\n\n if (this.profilesDir && groupKey) {\n const locales = profile.profileJson.locales?.length ? profile.profileJson.locales : [profile.defaultLocale]\n await this.runDeactivate(groupKey)\n const paths = this.profileFilePaths(groupKey, locales)\n this.handleCache?.(paths, id, true)\n this.eventBus?.emit('cache:purge', { paths, profileId: id })\n }\n await this.audit.log({ tenantId: this.tenantId, action: 'profile.deactivated', resourceType: 'profile', resourceId: id })\n this.eventBus?.emit('profile.deactivated', updated)\n return updated\n }\n\n private async runDeactivate(complianceGroup: string): Promise<void> {\n if (!this.profilesDir) return\n const msg: WorkerMessage = {\n action: 'deactivate',\n storagePath: join(this.profilesDir, '..'),\n tenantId: this.tenantId,\n profileId: '',\n version: 0,\n complianceGroup,\n profile: { id: '', defaultLocale: '', locales: [], cookies: {} },\n localeContent: {},\n ...(this.s3Config?.enabled ? { s3Config: this.s3Config } : {}),\n }\n await runProfileJsonWrite(msg)\n }\n\n async get(id: string): Promise<Profile | null> {\n return this.profiles.get(id)\n }\n\n /**\n * Resolves one locale's full public response. The default locale's content lives directly on\n * the DB row (`profileJson.mainBanner`/`gpcBanner`/`preferenceModal`) — no template lookups, no\n * live resolution. Any other locale's content lives only in that locale's on-disk version file;\n * this reads it directly, falling back to the default locale's content if the file is missing\n * (e.g. a locale listed but never actually authored yet). This method is not a hot path — the\n * public `/profiles/:tenant/:group/:locale` route serves the static file directly and only\n * falls back to this for previewing an inactive/versioned profile.\n */\n async getResolved(id: string, locale?: string): Promise<PublicProfileResponse | null> {\n const profile = await this.profiles.get(id)\n if (!profile) return null\n\n const currentLocale = locale ?? profile.defaultLocale\n\n if (this.localeCache) {\n const cached = this.localeCache.read(id, currentLocale)\n if (cached) return cached\n }\n\n const pj = profile.profileJson\n const cookies = await this.resolveCookies(pj)\n const locales = pj.locales?.length ? pj.locales : [profile.defaultLocale]\n\n const defaultContent: ResolvedLocaleContent = {\n mainBanner: pj.mainBanner,\n ...(pj.gpcBanner ? { gpcBanner: pj.gpcBanner } : {}),\n preferenceModal: pj.preferenceModal,\n }\n\n let content = defaultContent\n if (currentLocale !== profile.defaultLocale) {\n const raw = await this.getVersionFile(id, String(profile.version), currentLocale)\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as ResolvedLocaleContent\n content = {\n mainBanner: parsed.mainBanner,\n ...(parsed.gpcBanner ? { gpcBanner: parsed.gpcBanner } : {}),\n preferenceModal: parsed.preferenceModal,\n }\n } catch { /* corrupt/unreadable file — fall back to default locale content below */ }\n }\n }\n\n const response: PublicProfileResponse = {\n id: profile.id,\n version: profile.version,\n defaultLocale: profile.defaultLocale,\n currentLocale,\n locales,\n cookies,\n ...(pj.expiryDays !== undefined ? { expiryDays: pj.expiryDays } : {}),\n ...profileWideFields(pj),\n ...content,\n }\n\n if (this.localeCache) {\n try {\n this.localeCache.write(id, currentLocale, response)\n } catch (err) {\n // Non-fatal: the fast-path cache is a read optimization, not the source of truth —\n // getResolved() itself still returns the correct response either way. But a write\n // failure here usually means isPublicProfileResponse() and this response shape have\n // drifted apart, which is worth surfacing rather than silently degrading to zero cache.\n console.warn(`[Consenti] locale cache write failed for ${id}/${currentLocale}:`, err)\n }\n }\n\n return response\n }\n\n findActiveByComplianceGroup(complianceGroup: string): Promise<Profile | null> {\n return this.profiles.findActiveByComplianceGroup(this.tenantId, complianceGroup)\n }\n\n list(): Promise<Profile[]> {\n return this.profiles.list(this.tenantId)\n }\n\n listSummary(): Promise<ProfileSummary[]> {\n if (!this.storage) return Promise.resolve([])\n return this.storage.listProfilesSummary(this.tenantId)\n }\n\n /**\n * Lists every version snapshot of `profileId` on disk (newest first), read straight from\n * the version-directory tree — no DB query, `Profile.version` is just the current pointer.\n * Works for archived profiles too (DB row deleted, files still on disk) — the directory\n * tree is the only thing this reads.\n */\n async listVersions(profileId: string): Promise<ProfileVersionEntry[]> {\n if (!this.profilesDir) return []\n const dir = this.versionsRootDir(profileId)\n if (!existsSync(dir)) return []\n const versions: ProfileVersionEntry[] = []\n try {\n const entries = readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n const version = Number(entry.name)\n if (!Number.isInteger(version)) continue\n const vdir = join(dir, entry.name)\n const locales = readdirSync(vdir)\n .filter(f => f.endsWith('.json') && f !== 'default.json')\n .map(f => f.replace('.json', ''))\n let createdAt: string\n try {\n createdAt = statSync(vdir).mtime.toISOString()\n } catch {\n createdAt = new Date().toISOString()\n }\n versions.push({ version, createdAt, locales })\n }\n } catch {\n return []\n }\n return versions.sort((a, b) => b.version - a.version)\n }\n\n /** Works for archived profiles too — see `listVersions()`. */\n async getVersionFile(profileId: string, version: string, locale: string): Promise<string | null> {\n if (!this.profilesDir) return null\n const dir = join(this.versionsRootDir(profileId), version)\n const filePath = join(dir, `${locale}.json`)\n if (!existsSync(filePath)) {\n const defaultPath = join(dir, 'default.json')\n if (!existsSync(defaultPath)) return null\n return readFileSync(defaultPath, 'utf8')\n }\n return readFileSync(filePath, 'utf8')\n }\n\n /**\n * Profile-id directories on disk with no matching DB row — deleted profiles whose version\n * snapshots `delete()` never removes. Directory-only read (id, version count, last-modified\n * mtime) — no file content is opened here; that only happens once a specific version is\n * requested via `getVersionFile()`, which works for these ids too.\n */\n async listArchivedProfiles(): Promise<ArchivedProfileSummary[]> {\n if (!this.profilesDir) return []\n const root = join(this.profilesDir, this.tenantId)\n if (!existsSync(root)) return []\n\n const existingIds = new Set((await this.profiles.list(this.tenantId)).map(p => p.id))\n const results: ArchivedProfileSummary[] = []\n let entries: Dirent[]\n try {\n entries = readdirSync(root, { withFileTypes: true })\n } catch {\n return []\n }\n for (const entry of entries) {\n // complianceGroup hot-serve dirs are symlinks (junctions) — never real profile dirs.\n if (!entry.isDirectory() || existingIds.has(entry.name)) continue\n const dirPath = join(root, entry.name)\n if (lstatSync(dirPath).isSymbolicLink()) continue\n let versionDirs: Dirent[]\n try {\n versionDirs = readdirSync(dirPath, { withFileTypes: true })\n } catch {\n continue\n }\n const versions = versionDirs.filter(v => v.isDirectory() && Number.isInteger(Number(v.name)))\n if (versions.length === 0) continue // not a profile dir (no numbered version subdirs)\n let lastModified = new Date(0).toISOString()\n for (const v of versions) {\n try {\n const mtime = statSync(join(dirPath, v.name)).mtime.toISOString()\n if (mtime > lastModified) lastModified = mtime\n } catch { /* skip unreadable version dir */ }\n }\n results.push({ id: entry.name, versionCount: versions.length, lastModified })\n }\n return results.sort((a, b) => b.lastModified.localeCompare(a.lastModified))\n }\n\n /**\n * Seeds a default profile for a compliance group from the embedded English profile in\n * `@consenti/utils`, merged with locale text overlays for de/es/fr/ja. Every locale — including\n * non-English ones — is passed through `create()`'s normal `localeContent` path, the same as an\n * author-submitted profile; there is no separate \"seeded profile\" code path for locale content\n * anymore, which is what previously let a locale silently never make it into the served file.\n * Only creates the profile if no active profile already exists for the compliance group.\n * Safe to call repeatedly (idempotent).\n *\n * @param complianceGroup - One of the 8 compliance group IDs.\n */\n async seedDefaultProfile(complianceGroup: string): Promise<void> {\n const existing = await this.profiles.findActiveByComplianceGroup(this.tenantId, complianceGroup)\n if (existing) return\n\n const { DEFAULT_PROFILES, resolveLocaleTranslation } = await import('@consenti/utils/profiles')\n const embedded = DEFAULT_PROFILES[complianceGroup as keyof typeof DEFAULT_PROFILES]\n if (!embedded) return\n\n const enTranslation = embedded.translations['en']\n if (!enTranslation) return\n\n const otherLocales = ['de', 'es', 'fr', 'ja']\n const locales = ['en', ...otherLocales.filter(lang => resolveLocaleTranslation(complianceGroup, lang) !== undefined)]\n const localeContent: Record<string, LocaleContentInput> = {}\n for (const lang of otherLocales) {\n const resolved = resolveLocaleTranslation(complianceGroup, lang)\n if (resolved) {\n localeContent[lang] = {\n mainBanner: resolved.mainBanner as unknown as MainBanner,\n ...(resolved.gpcBanner ? { gpcBanner: resolved.gpcBanner as unknown as GpcBanner } : {}),\n preferenceModal: resolved.preferenceModal as unknown as PreferenceModal,\n }\n }\n }\n\n await this.create({\n name: `Default — ${complianceGroup}`,\n defaultLocale: embedded.defaultLocale,\n profileJson: {\n id: randomUUID(),\n defaultLocale: embedded.defaultLocale,\n complianceGroup: embedded.complianceGroup,\n gpcMode: embedded.gpcMode,\n cookies: embedded.cookies as unknown as CookieMap,\n ...(embedded.expiryDays !== undefined ? { expiryDays: embedded.expiryDays } : {}),\n locales,\n mainBanner: enTranslation.mainBanner as unknown as MainBanner,\n ...(enTranslation.gpcBanner ? { gpcBanner: enTranslation.gpcBanner as unknown as GpcBanner } : {}),\n preferenceModal: enTranslation.preferenceModal as unknown as PreferenceModal,\n isActive: true,\n },\n ...(Object.keys(localeContent).length > 0 ? { localeContent } : {}),\n })\n }\n\n /**\n * Seeds default profiles for all 8 compliance groups.\n * Skips any group that already has an active profile (idempotent).\n */\n async seedAllDefaults(): Promise<void> {\n const { COMPLIANCE_GROUP_IDS } = await import('@consenti/utils')\n await Promise.all(COMPLIANCE_GROUP_IDS.map(g => this.seedDefaultProfile(g)))\n }\n}\n","import { Worker } from 'node:worker_threads'\nimport { fileURLToPath } from 'node:url'\nimport { join, dirname } from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { runWorkerAction } from './profile-json-worker-core.js'\nimport type { WorkerMessage } from './profile-json-worker-core.js'\n\ntype WorkerResult =\n | { ok: true; action: WorkerMessage['action'] }\n | { ok: false; action: WorkerMessage['action']; error: string }\n\nconst _dir = dirname(fileURLToPath(import.meta.url))\nconst JS_WORKER = join(_dir, 'profile-json.worker.js')\n// In dev (tsx), only the .ts file exists; skip the worker thread entirely and call the core logic directly.\nconst IS_DEV = !existsSync(JS_WORKER)\n\n/**\n * Writes/activates/deactivates profile JSON files.\n * In dev mode runs inline (no worker thread). In production uses a worker thread.\n * Throws if the operation fails so callers can surface the error to the API client.\n */\nexport async function runProfileJsonWrite(msg: WorkerMessage): Promise<void> {\n if (IS_DEV) {\n await runWorkerAction(msg)\n return\n }\n\n return new Promise<void>((resolve, reject) => {\n const worker = new Worker(JS_WORKER)\n worker.postMessage(msg)\n worker.once('message', (result: WorkerResult) => {\n void worker.terminate()\n if (result.ok) {\n resolve()\n } else {\n reject(new Error(`[profile-worker] ${result.action} failed: ${result.error}`))\n }\n })\n worker.once('error', err => {\n void worker.terminate()\n reject(err)\n })\n })\n}\n","import { writeFileSync, renameSync, mkdirSync, copyFileSync, rmSync, existsSync, readFileSync, lstatSync, symlinkSync, unlinkSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { randomUUID } from 'node:crypto'\nimport type { CookieMap, MainBanner, GpcBanner, PreferenceModal, S3ApiConfig, PublicProfileResponse } from '@consenti/types'\nimport { s3Put, s3Delete } from '../storage/s3/s3-client.js'\n\n/** One locale's resolved banner/modal content — see `ProfileService`'s `ResolvedLocaleContent`. */\ninterface ResolvedLocaleContent {\n mainBanner: MainBanner\n gpcBanner?: GpcBanner\n preferenceModal: PreferenceModal\n}\n\nexport interface WorkerMessage {\n action: 'write' | 'activate' | 'deactivate'\n /** Fields shared by every locale's file — locale-specific content lives in `localeContent`.\n * The profile-wide settings (`gpcMode`/`complianceGroup`/etc.) are written into every locale's\n * file identically, since they aren't locale-specific — see `ProfileService.profileWideFields`. */\n profile: {\n id: string\n defaultLocale: string\n locales: string[]\n cookies: CookieMap\n expiryDays?: number\n } & Pick<PublicProfileResponse,\n 'gpcMode' | 'complianceGroup' | 'darkMode' | 'allowReceipt' | 'enhanceAccessibility' |\n 'showFooterMetadata' | 'allowedOrigins' | 'complianceConfig' | 'dpdpa' | 'hidePoweredBy'\n >\n /** Resolved content for locales included in this save. Any locale in `profile.locales` not\n * present here is carried forward unchanged from `previousVersion`'s file — see\n * `writeVersionDir()`. */\n localeContent: Record<string, ResolvedLocaleContent>\n /** The version to carry forward untouched locale files from. Undefined on create (a new\n * profile has no previous version, so an unlisted locale there simply gets no file). */\n previousVersion?: number\n storagePath: string\n tenantId: string\n profileId: string\n /** Incremented in place on every save — this snapshot's version number. */\n version: number\n complianceGroup: string\n s3Config?: S3ApiConfig\n}\n\nfunction profilesRoot(storagePath: string, tenantId: string): string {\n return join(storagePath, 'profiles', tenantId)\n}\n\n/** One snapshot's on-disk directory: grouped under the profile's stable id, keyed by version number. */\nfunction versionDir(storagePath: string, tenantId: string, profileId: string, version: number): string {\n return join(profilesRoot(storagePath, tenantId), profileId, String(version))\n}\n\nfunction complianceDir(storagePath: string, tenantId: string, complianceGroup: string): string {\n return join(profilesRoot(storagePath, tenantId), complianceGroup)\n}\n\nfunction s3Key(tenantId: string, segment: string, locale: string): string {\n return `profiles/${tenantId}/${segment}/${locale}.json`\n}\n\nfunction writeJsonFile(filePath: string, content: string): void {\n const tmp = filePath + '.tmp'\n writeFileSync(tmp, content, 'utf8')\n JSON.parse(content)\n renameSync(tmp, filePath)\n}\n\nconst PROFILE_WIDE_KEYS = [\n 'gpcMode', 'complianceGroup', 'darkMode', 'allowReceipt', 'enhanceAccessibility',\n 'showFooterMetadata', 'allowedOrigins', 'complianceConfig', 'dpdpa', 'hidePoweredBy',\n] as const\n\n/** Copies whichever profile-wide settings are actually set — same fields on every locale's file,\n * since none of these are locale-specific. */\nfunction profileWideFields(profile: WorkerMessage['profile']): Partial<Pick<WorkerMessage['profile'], typeof PROFILE_WIDE_KEYS[number]>> {\n const result: Partial<Pick<WorkerMessage['profile'], typeof PROFILE_WIDE_KEYS[number]>> = {}\n for (const key of PROFILE_WIDE_KEYS) {\n const value = profile[key]\n if (value !== undefined) (result as Record<string, unknown>)[key] = value\n }\n return result\n}\n\n/**\n * Writes every locale's on-disk file for one version in a single atomic pass — each locale gets\n * its own fully-resolved content directly (`localeContent[locale]`), never a merge/overlay onto\n * another locale's content. A locale in `profile.locales` with no entry in `localeContent` (a tab\n * the caller didn't touch this save) is carried forward byte-for-byte from `previousVersion`'s\n * file instead of being silently dropped.\n */\nexport async function writeVersionDir(msg: WorkerMessage): Promise<void> {\n const { storagePath, tenantId, profileId, version, profile, localeContent, previousVersion, s3Config } = msg\n const dir = versionDir(storagePath, tenantId, profileId, version)\n mkdirSync(dir, { recursive: true })\n\n const { locales, defaultLocale, cookies, expiryDays } = profile\n\n for (const locale of locales) {\n let content: string\n const resolved = localeContent[locale]\n if (resolved) {\n const doc = {\n id: profile.id,\n version,\n defaultLocale,\n currentLocale: locale,\n locales,\n cookies,\n ...(expiryDays !== undefined ? { expiryDays } : {}),\n ...profileWideFields(profile),\n mainBanner: resolved.mainBanner,\n ...(resolved.gpcBanner ? { gpcBanner: resolved.gpcBanner } : {}),\n preferenceModal: resolved.preferenceModal,\n }\n content = JSON.stringify(doc, null, 2)\n } else if (previousVersion !== undefined) {\n const prevPath = join(versionDir(storagePath, tenantId, profileId, previousVersion), `${locale}.json`)\n if (!existsSync(prevPath)) continue\n content = readFileSync(prevPath, 'utf8')\n } else {\n continue // create-time, a listed locale with no submitted content — nothing to write yet\n }\n\n const filePath = join(dir, `${locale}.json`)\n writeJsonFile(filePath, content)\n\n if (locale === defaultLocale) {\n writeJsonFile(join(dir, 'default.json'), content)\n }\n\n if (s3Config?.enabled) {\n await s3Put(s3Key(tenantId, `${profileId}/${version}`, locale), content, s3Config)\n if (locale === defaultLocale) {\n await s3Put(s3Key(tenantId, `${profileId}/${version}`, 'default'), content, s3Config)\n }\n }\n }\n\n if (!existsSync(join(dir, 'default.json'))) {\n const fallbackLocale = locales.includes(defaultLocale) ? defaultLocale : (locales[0] ?? '')\n if (fallbackLocale) {\n const fallbackPath = join(dir, `${fallbackLocale}.json`)\n if (existsSync(fallbackPath)) {\n copyFileSync(fallbackPath, join(dir, 'default.json'))\n }\n }\n }\n}\n\n/**\n * Removes whatever currently sits at `linkPath` if it isn't already a symlink — a one-time\n * migration step for directories written by the old copy-based activation. `rename()` can't\n * atomically replace a non-empty real directory, only a symlink (or nothing), so this must run\n * before the first symlink swap for a given compliance group.\n */\nfunction clearNonSymlink(linkPath: string): void {\n if (!existsSync(linkPath)) return\n if (!lstatSync(linkPath).isSymbolicLink()) rmSync(linkPath, { recursive: true, force: true })\n}\n\n/**\n * Atomically points `{complianceGroup}/` at this version's already-written, already-verified\n * directory — a directory symlink swap (junction on Windows), not a file copy. The old target\n * is left untouched on disk (it's the previous version's own directory), so there's no window\n * where the live path is missing or a mix of old/new files, and no separate cleanup step for\n * locales that existed on the previous version but not this one.\n */\nexport async function activateComplianceGroup(msg: WorkerMessage): Promise<void> {\n const { storagePath, tenantId, profileId, version, complianceGroup, s3Config } = msg\n const srcDir = resolve(versionDir(storagePath, tenantId, profileId, version))\n const linkPath = complianceDir(storagePath, tenantId, complianceGroup)\n\n mkdirSync(profilesRoot(storagePath, tenantId), { recursive: true })\n clearNonSymlink(linkPath)\n\n const tmpLinkPath = `${linkPath}.new-${randomUUID().slice(0, 8)}`\n symlinkSync(srcDir, tmpLinkPath, 'junction')\n renameSync(tmpLinkPath, linkPath)\n\n // S3 has no symlink equivalent — a single pointer object plays the same role. Readers\n // (our own route, or an external CDN/edge function) resolve this, then fetch the versioned\n // key directly; the per-locale content itself was already written to that key by writeVersionDir.\n if (s3Config?.enabled) {\n const pointer = JSON.stringify({ profileId, version })\n await s3Put(s3Key(tenantId, complianceGroup, 'pointer'), pointer, s3Config)\n }\n}\n\nexport async function deactivateComplianceGroup(msg: WorkerMessage): Promise<void> {\n const { storagePath, tenantId, complianceGroup, s3Config } = msg\n const linkPath = complianceDir(storagePath, tenantId, complianceGroup)\n\n if (existsSync(linkPath)) {\n if (lstatSync(linkPath).isSymbolicLink()) unlinkSync(linkPath)\n else rmSync(linkPath, { recursive: true, force: true }) // migration fallback, see clearNonSymlink\n }\n\n if (s3Config?.enabled) {\n await s3Delete(s3Key(tenantId, complianceGroup, 'pointer'), s3Config)\n }\n}\n\nexport async function runWorkerAction(msg: WorkerMessage): Promise<void> {\n if (msg.action === 'write') {\n await writeVersionDir(msg)\n } else if (msg.action === 'activate') {\n await writeVersionDir(msg)\n await activateComplianceGroup(msg)\n } else if (msg.action === 'deactivate') {\n await deactivateComplianceGroup(msg)\n }\n}\n","import { createHmac, createHash } from 'node:crypto'\nimport type { S3ApiConfig } from '@consenti/types'\n\ntype S3Config = Pick<S3ApiConfig, 'region' | 'bucketName' | 'accessKeyId' | 'secretAccessKey' | 'sessionToken'>\n\nfunction sha256Hex(data: string | Buffer): string {\n return createHash('sha256').update(data).digest('hex')\n}\n\nfunction hmacSha256(key: Buffer | string, data: string): Buffer {\n return createHmac('sha256', key).update(data).digest()\n}\n\nfunction toAmzDate(d: Date): { amzDate: string; dateStamp: string } {\n const iso = d.toISOString().replace(/[-:]/g, '').slice(0, 15) + 'Z'\n return { amzDate: iso, dateStamp: iso.slice(0, 8) }\n}\n\nfunction getSigningKey(secretKey: string, dateStamp: string, region: string, service: string): Buffer {\n const kDate = hmacSha256(`AWS4${secretKey}`, dateStamp)\n const kRegion = hmacSha256(kDate, region)\n const kService = hmacSha256(kRegion, service)\n return hmacSha256(kService, 'aws4_request')\n}\n\ninterface S3RequestParams {\n method: 'PUT' | 'GET' | 'HEAD' | 'DELETE'\n key: string\n body?: Buffer | string\n contentType?: string\n config: S3Config\n}\n\nfunction buildSignedHeaders(params: S3RequestParams): Record<string, string> {\n const { method, key, body, contentType, config } = params\n const now = new Date()\n const { amzDate, dateStamp } = toAmzDate(now)\n const host = `${config.bucketName}.s3.${config.region}.amazonaws.com`\n\n const payloadHash = body\n ? sha256Hex(Buffer.isBuffer(body) ? body : Buffer.from(body as string, 'utf8'))\n : sha256Hex('')\n\n const signedHeaderMap: Record<string, string> = {\n host,\n 'x-amz-date': amzDate,\n 'x-amz-content-sha256': payloadHash,\n }\n if (config.sessionToken) signedHeaderMap['x-amz-security-token'] = config.sessionToken\n if (contentType && method === 'PUT') signedHeaderMap['content-type'] = contentType\n\n const sortedKeys = Object.keys(signedHeaderMap).sort()\n const canonicalHeaders = sortedKeys.map(k => `${k}:${signedHeaderMap[k]}\\n`).join('')\n const signedHeadersList = sortedKeys.join(';')\n\n const canonicalRequest = [\n method,\n `/${encodeURIComponent(key).replace(/%2F/g, '/')}`,\n '',\n canonicalHeaders,\n signedHeadersList,\n payloadHash,\n ].join('\\n')\n\n const credentialScope = `${dateStamp}/${config.region}/s3/aws4_request`\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n amzDate,\n credentialScope,\n sha256Hex(canonicalRequest),\n ].join('\\n')\n\n const signingKey = getSigningKey(config.secretAccessKey, dateStamp, config.region, 's3')\n const signature = createHmac('sha256', signingKey).update(stringToSign).digest('hex')\n\n const authorization = [\n `AWS4-HMAC-SHA256 Credential=${config.accessKeyId}/${credentialScope}`,\n `SignedHeaders=${signedHeadersList}`,\n `Signature=${signature}`,\n ].join(', ')\n\n return { ...signedHeaderMap, authorization }\n}\n\nfunction s3Url(key: string, config: S3Config): string {\n const encodedKey = encodeURIComponent(key).replace(/%2F/g, '/')\n return `https://${config.bucketName}.s3.${config.region}.amazonaws.com/${encodedKey}`\n}\n\nexport async function s3Put(key: string, body: Buffer | string, config: S3Config, contentType = 'application/json'): Promise<void> {\n const headers = buildSignedHeaders({ method: 'PUT', key, body, contentType, config })\n const bodyStr = typeof body === 'string' ? body : body.toString('utf8')\n const res = await fetch(s3Url(key, config), {\n method: 'PUT',\n headers: { ...headers, 'content-length': String(Buffer.byteLength(bodyStr, 'utf8')) },\n body: bodyStr,\n })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`S3 PUT ${key} failed: ${res.status} ${text}`)\n }\n}\n\nexport async function s3Get(key: string, config: S3Config): Promise<Buffer> {\n const headers = buildSignedHeaders({ method: 'GET', key, config })\n const res = await fetch(s3Url(key, config), { method: 'GET', headers })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`S3 GET ${key} failed: ${res.status} ${text}`)\n }\n const buf = await res.arrayBuffer()\n return Buffer.from(buf)\n}\n\nexport async function s3Head(key: string, config: S3Config): Promise<boolean> {\n const headers = buildSignedHeaders({ method: 'HEAD', key, config })\n const res = await fetch(s3Url(key, config), { method: 'HEAD', headers })\n return res.ok\n}\n\nexport async function s3Delete(key: string, config: S3Config): Promise<void> {\n const headers = buildSignedHeaders({ method: 'DELETE', key, config })\n const res = await fetch(s3Url(key, config), { method: 'DELETE', headers })\n if (!res.ok && res.status !== 404) {\n const text = await res.text().catch(() => '')\n throw new Error(`S3 DELETE ${key} failed: ${res.status} ${text}`)\n }\n}\n","export const TIMEZONE_TO_COUNTRY: Readonly<Record<string, string>> = {\n // Europe\n 'Europe/Amsterdam': 'NL', 'Europe/Andorra': 'AD', 'Europe/Athens': 'GR',\n 'Europe/Belgrade': 'RS', 'Europe/Berlin': 'DE', 'Europe/Bratislava': 'SK',\n 'Europe/Brussels': 'BE', 'Europe/Bucharest': 'RO', 'Europe/Budapest': 'HU',\n 'Europe/Busingen': 'DE', 'Europe/Chisinau': 'MD', 'Europe/Copenhagen': 'DK',\n 'Europe/Dublin': 'IE', 'Europe/Gibraltar': 'GI', 'Europe/Guernsey': 'GG',\n 'Europe/Helsinki': 'FI', 'Europe/Isle_of_Man': 'IM','Europe/Istanbul': 'TR',\n 'Europe/Jersey': 'JE', 'Europe/Kaliningrad': 'RU','Europe/Kiev': 'UA',\n 'Europe/Kyiv': 'UA', 'Europe/Lisbon': 'PT', 'Europe/Ljubljana': 'SI',\n 'Europe/London': 'GB', 'Europe/Luxembourg': 'LU', 'Europe/Madrid': 'ES',\n 'Europe/Malta': 'MT', 'Europe/Mariehamn': 'FI', 'Europe/Minsk': 'BY',\n 'Europe/Monaco': 'MC', 'Europe/Moscow': 'RU', 'Europe/Nicosia': 'CY',\n 'Europe/Oslo': 'NO', 'Europe/Paris': 'FR', 'Europe/Podgorica': 'ME',\n 'Europe/Prague': 'CZ', 'Europe/Riga': 'LV', 'Europe/Rome': 'IT',\n 'Europe/Samara': 'RU', 'Europe/San_Marino': 'SM', 'Europe/Sarajevo': 'BA',\n 'Europe/Saratov': 'RU', 'Europe/Simferopol': 'UA', 'Europe/Skopje': 'MK',\n 'Europe/Sofia': 'BG', 'Europe/Stockholm': 'SE', 'Europe/Tallinn': 'EE',\n 'Europe/Tirane': 'AL', 'Europe/Ulyanovsk': 'RU', 'Europe/Uzhgorod': 'UA',\n 'Europe/Vaduz': 'LI', 'Europe/Vatican': 'VA', 'Europe/Vienna': 'AT',\n 'Europe/Vilnius': 'LT', 'Europe/Volgograd': 'RU', 'Europe/Warsaw': 'PL',\n 'Europe/Zagreb': 'HR', 'Europe/Zaporozhye': 'UA', 'Europe/Zurich': 'CH',\n 'Atlantic/Reykjavik': 'IS', 'Atlantic/Faroe': 'FO',\n // Americas — US\n 'America/New_York': 'US', 'America/Chicago': 'US', 'America/Denver': 'US',\n 'America/Los_Angeles': 'US', 'America/Anchorage': 'US', 'America/Adak': 'US',\n 'America/Honolulu': 'US', 'America/Phoenix': 'US', 'America/Detroit': 'US',\n 'America/Indiana/Indianapolis': 'US', 'America/Indiana/Knox': 'US',\n 'America/Indiana/Marengo': 'US', 'America/Indiana/Petersburg': 'US',\n 'America/Indiana/Tell_City': 'US', 'America/Indiana/Vevay': 'US',\n 'America/Indiana/Vincennes': 'US', 'America/Indiana/Winamac': 'US',\n 'America/Kentucky/Louisville': 'US', 'America/Kentucky/Monticello': 'US',\n 'America/North_Dakota/Beulah': 'US', 'America/North_Dakota/Center': 'US',\n 'America/North_Dakota/New_Salem': 'US', 'America/Boise': 'US',\n 'America/Menominee': 'US', 'America/Nome': 'US', 'America/Sitka': 'US',\n 'America/Yakutat': 'US', 'America/Juneau': 'US', 'America/Metlakatla': 'US',\n // Americas — Canada\n 'America/Toronto': 'CA', 'America/Vancouver': 'CA', 'America/Winnipeg': 'CA',\n 'America/Halifax': 'CA', 'America/St_Johns': 'CA', 'America/Edmonton': 'CA',\n 'America/Regina': 'CA', 'America/Whitehorse': 'CA', 'America/Yellowknife': 'CA',\n 'America/Iqaluit': 'CA', 'America/Moncton': 'CA', 'America/Glace_Bay': 'CA',\n 'America/Goose_Bay': 'CA', 'America/Swift_Current': 'CA', 'America/Dawson': 'CA',\n 'America/Cambridge_Bay': 'CA', 'America/Dawson_Creek': 'CA', 'America/Fort_Nelson': 'CA',\n 'America/Creston': 'CA', 'America/Rankin_Inlet': 'CA', 'America/Resolute': 'CA',\n 'America/Pangnirtung': 'CA', 'America/Nipigon': 'CA',\n // Americas — Mexico\n 'America/Mexico_City': 'MX', 'America/Cancun': 'MX', 'America/Monterrey': 'MX',\n 'America/Merida': 'MX', 'America/Chihuahua': 'MX', 'America/Hermosillo': 'MX',\n 'America/Mazatlan': 'MX', 'America/Ojinaga': 'MX', 'America/Bahia_Banderas': 'MX',\n 'America/Matamoros': 'MX', 'America/Tijuana': 'MX',\n // Americas — South America\n 'America/Sao_Paulo': 'BR', 'America/Manaus': 'BR', 'America/Belem': 'BR',\n 'America/Fortaleza': 'BR', 'America/Recife': 'BR', 'America/Maceio': 'BR',\n 'America/Bahia': 'BR', 'America/Porto_Velho': 'BR', 'America/Boa_Vista': 'BR',\n 'America/Campo_Grande': 'BR', 'America/Cuiaba': 'BR', 'America/Santarem': 'BR',\n 'America/Rio_Branco': 'BR', 'America/Eirunepe': 'BR', 'America/Noronha': 'BR',\n 'America/Argentina/Buenos_Aires': 'AR', 'America/Argentina/Cordoba': 'AR',\n 'America/Argentina/Salta': 'AR', 'America/Argentina/Jujuy': 'AR',\n 'America/Argentina/Tucuman': 'AR', 'America/Argentina/Catamarca': 'AR',\n 'America/Argentina/La_Rioja': 'AR', 'America/Argentina/San_Juan': 'AR',\n 'America/Argentina/Mendoza': 'AR', 'America/Argentina/San_Luis': 'AR',\n 'America/Argentina/Rio_Gallegos': 'AR', 'America/Argentina/Ushuaia': 'AR',\n 'America/Bogota': 'CO', 'America/Lima': 'PE', 'America/Santiago': 'CL',\n 'America/Caracas': 'VE', 'America/La_Paz': 'BO', 'America/Asuncion': 'PY',\n 'America/Montevideo': 'UY', 'America/Guayaquil': 'EC', 'America/Guyana': 'GY',\n 'America/Paramaribo': 'SR', 'America/Cayenne': 'GF',\n // Americas — Central America & Caribbean\n 'America/Panama': 'PA', 'America/Costa_Rica': 'CR', 'America/Guatemala': 'GT',\n 'America/El_Salvador': 'SV', 'America/Tegucigalpa': 'HN', 'America/Managua': 'NI',\n 'America/Belize': 'BZ', 'America/Havana': 'CU', 'America/Jamaica': 'JM',\n 'America/Nassau': 'BS', 'America/Port_of_Spain': 'TT', 'America/Santo_Domingo': 'DO',\n 'America/Port-au-Prince': 'HT', 'America/Barbados': 'BB', 'America/Martinique': 'MQ',\n 'America/Guadeloupe': 'GP', 'America/Puerto_Rico': 'PR', 'America/St_Kitts': 'KN',\n // Asia — South Asia\n 'Asia/Kolkata': 'IN', 'Asia/Calcutta': 'IN', 'Asia/Karachi': 'PK',\n 'Asia/Dhaka': 'BD', 'Asia/Colombo': 'LK', 'Asia/Kathmandu': 'NP',\n 'Asia/Kabul': 'AF',\n // Asia — East Asia\n 'Asia/Tokyo': 'JP', 'Asia/Seoul': 'KR', 'Asia/Shanghai': 'CN',\n 'Asia/Hong_Kong': 'HK', 'Asia/Macau': 'MO', 'Asia/Taipei': 'TW',\n 'Asia/Ulaanbaatar': 'MN', 'Asia/Choibalsan': 'MN', 'Asia/Hovd': 'MN',\n // Asia — Southeast Asia\n 'Asia/Singapore': 'SG', 'Asia/Bangkok': 'TH', 'Asia/Jakarta': 'ID',\n 'Asia/Kuala_Lumpur': 'MY', 'Asia/Manila': 'PH', 'Asia/Ho_Chi_Minh': 'VN',\n 'Asia/Saigon': 'VN', 'Asia/Phnom_Penh': 'KH', 'Asia/Vientiane': 'LA',\n 'Asia/Rangoon': 'MM', 'Asia/Yangon': 'MM', 'Asia/Pontianak': 'ID',\n 'Asia/Makassar': 'ID', 'Asia/Jayapura': 'ID', 'Asia/Kuching': 'MY',\n 'Asia/Brunei': 'BN', 'Asia/Dili': 'TL',\n // Asia — Central Asia & Middle East\n 'Asia/Tashkent': 'UZ', 'Asia/Almaty': 'KZ', 'Asia/Qyzylorda': 'KZ',\n 'Asia/Aqtau': 'KZ', 'Asia/Aqtobe': 'KZ', 'Asia/Atyrau': 'KZ',\n 'Asia/Oral': 'KZ', 'Asia/Ashgabat': 'TM', 'Asia/Dushanbe': 'TJ',\n 'Asia/Bishkek': 'KG', 'Asia/Yekaterinburg': 'RU',\n 'Asia/Tehran': 'IR', 'Asia/Baghdad': 'IQ', 'Asia/Riyadh': 'SA',\n 'Asia/Dubai': 'AE', 'Asia/Kuwait': 'KW', 'Asia/Qatar': 'QA',\n 'Asia/Bahrain': 'BH', 'Asia/Muscat': 'OM', 'Asia/Beirut': 'LB',\n 'Asia/Jerusalem': 'IL', 'Asia/Tel_Aviv': 'IL', 'Asia/Amman': 'JO',\n 'Asia/Damascus': 'SY', 'Asia/Nicosia': 'CY', 'Asia/Famagusta': 'CY',\n 'Asia/Baku': 'AZ', 'Asia/Yerevan': 'AM', 'Asia/Tbilisi': 'GE',\n 'Asia/Samarkand': 'UZ',\n // Africa\n 'Africa/Cairo': 'EG', 'Africa/Casablanca': 'MA', 'Africa/Lagos': 'NG',\n 'Africa/Nairobi': 'KE', 'Africa/Johannesburg': 'ZA', 'Africa/Addis_Ababa': 'ET',\n 'Africa/Algiers': 'DZ', 'Africa/Tunis': 'TN', 'Africa/Tripoli': 'LY',\n 'Africa/Khartoum': 'SD', 'Africa/Accra': 'GH', 'Africa/Dar_es_Salaam': 'TZ',\n 'Africa/Abidjan': 'CI', 'Africa/Bamako': 'ML', 'Africa/Banjul': 'GM',\n 'Africa/Conakry': 'GN', 'Africa/Dakar': 'SN', 'Africa/Freetown': 'SL',\n 'Africa/Lome': 'TG', 'Africa/Monrovia': 'LR', 'Africa/Nouakchott': 'MR',\n 'Africa/Ouagadougou': 'BF', 'Africa/Sao_Tome': 'ST',\n 'Africa/Bissau': 'GW', 'Africa/Bangui': 'CF', 'Africa/Brazzaville': 'CG',\n 'Africa/Douala': 'CM', 'Africa/Kinshasa': 'CD', 'Africa/Libreville': 'GA',\n 'Africa/Luanda': 'AO', 'Africa/Malabo': 'GQ', 'Africa/Ndjamena': 'TD',\n 'Africa/Niamey': 'NE', 'Africa/Porto-Novo': 'BJ',\n 'Africa/Blantyre': 'MW', 'Africa/Bujumbura': 'BI', 'Africa/Gaborone': 'BW',\n 'Africa/Harare': 'ZW', 'Africa/Kampala': 'UG', 'Africa/Kigali': 'RW',\n 'Africa/Lubumbashi': 'CD', 'Africa/Lusaka': 'ZM', 'Africa/Maputo': 'MZ',\n 'Africa/Maseru': 'LS', 'Africa/Mbabane': 'SZ', 'Africa/Mogadishu': 'SO',\n 'Africa/Windhoek': 'NA', 'Africa/Asmara': 'ER', 'Africa/Djibouti': 'DJ',\n 'Africa/El_Aaiun': 'EH', 'Africa/Juba': 'SS', 'Africa/Ceuta': 'ES',\n // Oceania\n 'Australia/Sydney': 'AU', 'Australia/Melbourne': 'AU', 'Australia/Brisbane': 'AU',\n 'Australia/Perth': 'AU', 'Australia/Adelaide': 'AU', 'Australia/Darwin': 'AU',\n 'Australia/Hobart': 'AU', 'Australia/Canberra': 'AU', 'Australia/Broken_Hill': 'AU',\n 'Australia/Lord_Howe': 'AU', 'Australia/Lindeman': 'AU', 'Australia/Eucla': 'AU',\n 'Pacific/Auckland': 'NZ', 'Pacific/Chatham': 'NZ',\n 'Pacific/Fiji': 'FJ', 'Pacific/Port_Moresby': 'PG', 'Pacific/Bougainville': 'PG',\n 'Pacific/Guam': 'GU', 'Pacific/Saipan': 'MP', 'Pacific/Pago_Pago': 'AS',\n 'Pacific/Apia': 'WS', 'Pacific/Tongatapu': 'TO', 'Pacific/Fakaofo': 'TK',\n 'Pacific/Niue': 'NU', 'Pacific/Rarotonga': 'CK', 'Pacific/Tahiti': 'PF',\n 'Pacific/Marquesas': 'PF', 'Pacific/Gambier': 'PF', 'Pacific/Honolulu': 'US',\n 'Pacific/Johnston': 'US', 'Pacific/Midway': 'UM', 'Pacific/Wake': 'UM',\n 'Pacific/Tarawa': 'KI', 'Pacific/Enderbury': 'KI', 'Pacific/Kiritimati': 'KI',\n 'Pacific/Efate': 'VU', 'Pacific/Guadalcanal': 'SB', 'Pacific/Noumea': 'NC',\n 'Pacific/Nauru': 'NR', 'Pacific/Palau': 'PW', 'Pacific/Chuuk': 'FM',\n 'Pacific/Pohnpei': 'FM', 'Pacific/Kosrae': 'FM', 'Pacific/Majuro': 'MH',\n 'Pacific/Kwajalein': 'MH', 'Pacific/Funafuti': 'TV', 'Pacific/Wallis': 'WF',\n // Russia (non-European)\n 'Asia/Novosibirsk': 'RU', 'Asia/Omsk': 'RU', 'Asia/Krasnoyarsk': 'RU',\n 'Asia/Irkutsk': 'RU', 'Asia/Chita': 'RU', 'Asia/Yakutsk': 'RU',\n 'Asia/Khandyga': 'RU', 'Asia/Vladivostok': 'RU', 'Asia/Ust-Nera': 'RU',\n 'Asia/Magadan': 'RU', 'Asia/Sakhalin': 'RU', 'Asia/Srednekolymsk': 'RU',\n 'Asia/Kamchatka': 'RU', 'Asia/Anadyr': 'RU',\n} as const\n","import type { CountryResolverFn, ComplianceConfig, ComplianceMapData, GeoResult } from '@consenti/types'\nimport { EMBEDDED_COMPLIANCE_MAP } from '@consenti/utils'\nimport { TIMEZONE_TO_COUNTRY } from '../data/timezone-country'\n\ntype JurisdictionMap = typeof EMBEDDED_COMPLIANCE_MAP | ComplianceMapData\n\nexport interface GeoContext {\n ip: string\n language: string\n timezone: string\n}\n\nexport interface ResolvedGeo {\n country: string\n region?: string\n regulation?: string\n complianceGroup: string\n locale?: string | null\n}\n\nconst LANG_TO_COUNTRY: Readonly<Record<string, string>> = {\n 'ar': 'SA', 'cs': 'CZ', 'da': 'DK', 'de': 'DE', 'el': 'GR',\n 'fi': 'FI', 'fr': 'FR', 'he': 'IL', 'hi': 'IN', 'hu': 'HU',\n 'id': 'ID', 'it': 'IT', 'ja': 'JP', 'ko': 'KR', 'nl': 'NL',\n 'no': 'NO', 'pl': 'PL', 'pt': 'BR', 'ro': 'RO', 'ru': 'RU',\n 'sv': 'SE', 'th': 'TH', 'tr': 'TR', 'uk': 'UA', 'vi': 'VN', 'zh': 'CN',\n} as const\n\nexport class GeoResolverService {\n private readonly provider: Exclude<ComplianceConfig['geoDataProvider'], undefined>\n\n constructor(\n provider: ComplianceConfig['geoDataProvider'],\n private readonly map: JurisdictionMap,\n ) {\n // 'default' and 'timezone' are both the timezone+language heuristic\n this.provider = provider ?? 'default'\n }\n\n async resolve(ctx: GeoContext): Promise<ResolvedGeo> {\n let country = ''\n let region: string | undefined\n let regulation: string | undefined\n let locale: string | null = null\n\n if (typeof this.provider === 'function') {\n const result: GeoResult = await (this.provider as CountryResolverFn)(ctx)\n country = result.country ?? ''\n region = result.region ?? undefined\n locale = result.locale\n } else {\n switch (this.provider) {\n case 'default':\n case 'timezone':\n ;({ country } = resolveFromTimezone(ctx.timezone))\n if (!country) {\n ;({ country, region } = resolveFromLanguage(ctx.language))\n }\n break\n case 'language':\n ;({ country, region } = resolveFromLanguage(ctx.language))\n break\n case 'geoip':\n ;({ country, region } = await resolveFromGeoIPLite(ctx.ip))\n break\n case 'hosted-geoip-lite':\n ;({ country, region } = await resolveFromHosted(ctx.ip))\n break\n case 'maxmind':\n ;({ country, region } = await resolveFromMaxmind(ctx.ip))\n break\n }\n }\n\n const geo: ResolvedGeo = { country, complianceGroup: this.resolveGroup(country, region) }\n if (region !== undefined) geo.region = region\n if (regulation !== undefined) geo.regulation = regulation\n if (locale !== null) geo.locale = locale\n return geo\n }\n\n private resolveGroup(country: string, region?: string): string {\n if (!country) return ''\n const countries = this.map.countries as Record<string, {\n complianceGroup: string\n default: string\n overriddenRegions?: Record<string, { complianceGroup: string }>\n }>\n const entry = countries[country]\n if (!entry) return ''\n if (region !== undefined) {\n // region was resolved — use override if present, else base country group\n return entry.overriddenRegions?.[region]?.complianceGroup ?? entry.complianceGroup\n }\n // region not available — use .default (most strict) so we never under-comply\n return entry.default\n }\n}\n\n// ─── Strategy implementations ─────────────────────────────────────────────────\n\nfunction resolveFromLanguage(language: string): { country: string; region?: string } {\n const first = language.split(',')[0]?.trim().split(';')[0]?.trim() ?? ''\n const parts = first.split('-')\n if (parts.length >= 2) {\n const tag = (parts[parts.length - 1] ?? '').toUpperCase()\n if (/^[A-Z]{2}$/.test(tag)) return { country: tag }\n }\n const country = LANG_TO_COUNTRY[(parts[0] ?? '').toLowerCase()] ?? ''\n return { country }\n}\n\nfunction resolveFromTimezone(timezone: string): { country: string } {\n return { country: TIMEZONE_TO_COUNTRY[timezone] ?? '' }\n}\n\nasync function resolveFromGeoIPLite(ip: string): Promise<{ country: string; region?: string }> {\n if (!ip) return { country: '' }\n try {\n // geoip-lite is an optional peer dep — types may not be installed\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const geoip = await import('geoip-lite') as {\n lookup(ip: string): { country?: string; region?: string } | null\n }\n const r = geoip.lookup(ip)\n const result: { country: string; region?: string } = { country: r?.country ?? '' }\n const reg = r?.region\n if (reg) result.region = reg\n return result\n } catch {\n console.warn('[Consenti] geoip-lite not installed — country detection skipped. Run: npm install geoip-lite')\n return { country: '' }\n }\n}\n\n// 'maxmind' — requires the `maxmind` npm package and a local GeoLite2-City.mmdb database.\n// See HOW_TO_MAXMIND.md for setup instructions.\nasync function resolveFromMaxmind(ip: string): Promise<{ country: string; region?: string }> {\n if (!ip) return { country: '' }\n try {\n // maxmind is an optional peer dep — dynamically imported to avoid hard dependency\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n const maxmind = await import('maxmind') as {\n open<T>(path: string): Promise<{ get(ip: string): T | null }>\n }\n const dbPath = process.env['MAXMIND_DB_PATH'] ?? './GeoLite2-City.mmdb'\n const reader = await maxmind.open<{\n country?: { iso_code?: string }\n subdivisions?: Array<{ iso_code?: string }>\n }>(dbPath)\n const r = reader.get(ip)\n const country = r?.country?.iso_code ?? ''\n const region = r?.subdivisions?.[0]?.iso_code\n const result: { country: string; region?: string } = { country }\n if (region) result.region = region\n return result\n } catch {\n console.warn('[Consenti] maxmind not available — country detection skipped. See HOW_TO_MAXMIND.md')\n return { country: '' }\n }\n}\n\n// 'hosted-geoip-lite' built-in: calls ipinfo.io with node:https (zero external dep).\n// For a custom token or endpoint, pass a CountryResolverFn closure instead.\nasync function resolveFromHosted(ip: string): Promise<{ country: string; region?: string }> {\n if (!ip) return { country: '' }\n return new Promise((resolve) => {\n import('node:https').then(({ default: https }) => {\n const url = `https://ipinfo.io/${encodeURIComponent(ip)}/json`\n const req = https.get(url, { timeout: 3000 }, (res) => {\n const chunks: Buffer[] = []\n res.on('data', (c: Buffer) => chunks.push(c))\n res.on('end', () => {\n try {\n const body = JSON.parse(Buffer.concat(chunks).toString()) as {\n country?: string\n region?: string\n }\n const result: { country: string; region?: string } = { country: body.country ?? '' }\n if (body.region) result.region = body.region\n resolve(result)\n } catch {\n resolve({ country: '' })\n }\n })\n })\n req.on('error', () => resolve({ country: '' }))\n req.on('timeout', () => { req.destroy(); resolve({ country: '' }) })\n }).catch(() => resolve({ country: '' }))\n })\n}\n","import { existsSync, readFileSync, readdirSync, unlinkSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { PublicProfileResponse } from '@consenti/types'\nimport { safeJsonWrite } from '../utils/safe-json-write'\n\nfunction isPublicProfileResponse(v: unknown): v is PublicProfileResponse {\n return (\n typeof v === 'object' &&\n v !== null &&\n typeof (v as Record<string, unknown>)['id'] === 'string' &&\n typeof (v as Record<string, unknown>)['version'] === 'number'\n )\n}\n\nexport class LocaleJsonCacheService {\n constructor(private readonly dir: string) {}\n\n read(profileId: string, locale: string): PublicProfileResponse | null {\n const path = this.path(profileId, locale)\n if (!existsSync(path)) return null\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown\n return isPublicProfileResponse(parsed) ? parsed : null\n } catch {\n return null\n }\n }\n\n write(profileId: string, locale: string, response: PublicProfileResponse): void {\n safeJsonWrite(this.path(profileId, locale), response, isPublicProfileResponse)\n }\n\n rebuild(profileId: string, responses: Map<string, PublicProfileResponse>): void {\n for (const [locale, response] of responses) {\n try {\n this.write(profileId, locale, response)\n } catch (err) {\n console.warn(`[Consenti] locale cache rebuild failed for ${profileId}/${locale}:`, err)\n }\n }\n }\n\n invalidate(profileId: string): void {\n try {\n const prefix = `${profileId}_`\n for (const file of readdirSync(this.dir)) {\n if (file.startsWith(prefix) && file.endsWith('.json')) {\n try { unlinkSync(join(this.dir, file)) } catch { /* ok */ }\n }\n }\n } catch { /* ok if dir doesn't exist */ }\n }\n\n private path(profileId: string, locale: string): string {\n return join(this.dir, `${profileId}_${locale}.json`)\n }\n}\n","import { copyFileSync, writeFileSync, renameSync, unlinkSync, readFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\n\nexport function safeJsonWrite(\n filePath: string,\n data: unknown,\n validateSchema: (parsed: unknown) => boolean,\n): void {\n const bak = filePath + '.bak'\n const tmp = filePath + '.tmp'\n mkdirSync(dirname(filePath), { recursive: true })\n const had = existsSync(filePath)\n if (had) copyFileSync(filePath, bak)\n try {\n writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf8')\n const parsed = JSON.parse(readFileSync(tmp, 'utf8')) as unknown\n if (!validateSchema(parsed)) throw new Error('Schema validation failed for ' + filePath)\n renameSync(tmp, filePath)\n if (had) { try { unlinkSync(bak) } catch { /* ok */ } }\n } catch (err) {\n if (had) { try { copyFileSync(bak, filePath); unlinkSync(bak) } catch { /* ok */ } }\n try { unlinkSync(tmp) } catch { /* ok */ }\n throw err\n }\n}\n","import type {\n ConsentDbRecord,\n ConsentValue,\n ConsentVerifyResult,\n CreateConsentInput,\n Profile,\n CookieMap,\n Category,\n} from '@consenti/types'\nimport { buildCookieCategoryIndex, getCookieLegalBasis, isMandatoryCookie } from '@consenti/utils'\nimport { signHmac } from '../utils/crypto'\n\nexport interface ConsentValidationResult {\n valid: boolean\n errors: string[]\n}\n\nexport function validateConsentInput(\n data: Pick<CreateConsentInput, 'consentJson'>,\n cookies: CookieMap,\n categoryIndex: Map<string, Category>,\n): ConsentValidationResult {\n const errors: string[] = []\n for (const [cookieId, status] of Object.entries(data.consentJson)) {\n if (!cookies[cookieId]) continue\n const legalBasis = getCookieLegalBasis(cookieId, categoryIndex)\n if (legalBasis === 'mandatory' && status !== 'granted') {\n errors.push(`Cookie \"${cookieId}\" is mandatory and cannot be \"${status}\"`)\n }\n if (status === 'objected' && legalBasis !== 'legitimate_interest') {\n errors.push(`Cookie \"${cookieId}\" has legal basis \"${legalBasis}\" — 'objected' is only valid for 'legitimate_interest'`)\n }\n }\n return { valid: errors.length === 0, errors }\n}\n\nexport function buildConsentValue(\n cookies: CookieMap,\n categoryIndex: Map<string, Category>,\n submitted: ConsentValue,\n gpcDetected: boolean,\n regulation?: string,\n): ConsentValue {\n const result: ConsentValue = {}\n for (const [id, cookie] of Object.entries(cookies)) {\n if (isMandatoryCookie(id, categoryIndex)) {\n result[id] = 'granted'\n continue\n }\n // CPRA: GPC triggers Do Not Sell + Do Not Share; sensitive data requires explicit opt-in\n if (regulation === 'cpra') {\n if (cookie.cpraCategory === 'sensitive') {\n // Sensitive data is opt-in even under CPRA — treat like GDPR (deny by default)\n const status = submitted[id]\n result[id] = (status === 'granted' || status === 'denied' || status === 'objected') ? status : 'denied'\n continue\n }\n if (gpcDetected && (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing')) {\n result[id] = 'denied'\n continue\n }\n }\n // GDPR/CCPA/DPDPA: honor listenGpc flag\n if (gpcDetected && cookie.listenGpc && regulation !== 'dpdpa') {\n result[id] = 'denied'\n continue\n }\n const status = submitted[id]\n if (status === 'granted' || status === 'denied' || status === 'objected') {\n result[id] = status\n } else {\n result[id] = 'denied'\n }\n }\n return result\n}\n\n/**\n * Canonical string signed/verified for a consent record's tamper-evidence signature. Built only\n * from fields known before the record is persisted (excludes DB-generated `id`/`createdAt`/\n * `updatedAt`), so the same payload can be computed both when signing at create/update time and\n * when re-verifying a stored record later. `consentJson` keys are sorted so the payload is\n * stable regardless of key insertion order across different storage backends.\n */\nexport function buildConsentSignaturePayload(record: {\n tenantId: string\n visitorId: string\n profileId: string\n locale: string\n consentJson: ConsentValue\n}): string {\n const sortedConsent = Object.keys(record.consentJson)\n .sort()\n .map(k => `${k}=${record.consentJson[k]}`)\n .join(',')\n return `${record.tenantId}|${record.visitorId}|${record.profileId}|${record.locale}|${sortedConsent}`\n}\n\n/**\n * `recordProfile` is the exact (immutable, possibly archived) profile snapshot the consent was\n * collected against — looked up by `record.profileId`, which never changes after the fact.\n * `activeProfile` is whatever is currently live for that snapshot's compliance group — if its id\n * no longer matches `recordProfile.id`, a newer edit has superseded it and the consent is stale.\n * `signingKey` is only checked when both it and `record.signature` are present — opt-in, and a\n * record signed before `consentSigningKey` was configured (or never signed) never fails this\n * check for its absence.\n */\nexport function verifyConsent(\n record: ConsentDbRecord,\n recordProfile: Profile,\n activeProfile: Profile | null,\n signingKey?: string,\n): ConsentVerifyResult {\n const reasons: ConsentVerifyResult['reasons'] = []\n\n if (!activeProfile || activeProfile.id !== recordProfile.id) {\n reasons.push('profile_changed')\n }\n\n if (isExpired(record, recordProfile)) {\n reasons.push('consent_expired')\n }\n\n if (signingKey && record.signature) {\n const expected = signHmac(buildConsentSignaturePayload(record), signingKey)\n if (expected !== record.signature) reasons.push('hmac_invalid')\n }\n\n return {\n valid: reasons.length === 0,\n reasons,\n ...(activeProfile ? { currentProfileId: activeProfile.id } : {}),\n consentProfileId: recordProfile.id,\n }\n}\n\nexport function buildTcfPayload(\n cookies: CookieMap,\n consentValue: ConsentValue,\n): { vendorConsents: number[]; purposeConsents: number[] } {\n const vendorSet = new Set<number>()\n const purposeSet = new Set<number>()\n for (const [id, cookie] of Object.entries(cookies)) {\n if (consentValue[id] !== 'granted') continue\n if (cookie.tcfVendorId) vendorSet.add(cookie.tcfVendorId)\n for (const p of cookie.tcfPurposes ?? []) purposeSet.add(p)\n }\n return {\n vendorConsents: [...vendorSet].sort((a, b) => a - b),\n purposeConsents: [...purposeSet].sort((a, b) => a - b),\n }\n}\n\nfunction isExpired(record: ConsentDbRecord, profile: Profile): boolean {\n const expiryDays = profile.profileJson.expiryDays\n if (!expiryDays) return false\n const createdMs = new Date(record.createdAt).getTime()\n const expiryMs = createdMs + expiryDays * 24 * 60 * 60 * 1000\n return Date.now() > expiryMs\n}\n","import type {\n ConsentDbRecord, ConsentVerifyResult, CreateConsentInput, UpdateConsentInput,\n ComplianceConfig, TcfConfig, CategoryMap, CookieMap, ProfileConfig, StorageAdapter,\n} from '@consenti/types'\nimport { buildConsentValue, verifyConsent, validateConsentInput, buildTcfPayload, buildConsentSignaturePayload } from '../core/consent-engine'\nimport { buildCookieCategoryIndex, encodeTcString } from '@consenti/utils'\nimport { getGvl } from '../tcf/gvl-cache'\nimport { signHmac } from '../utils/crypto'\nimport type { ConsentRepo } from '../repositories/consent.repo'\nimport type { VisitorRepo } from '../repositories/visitor.repo'\nimport type { ProfileRepo } from '../repositories/profile.repo'\nimport type { AuditRepo } from '../repositories/audit.repo'\nimport type { PluginEngine } from '../core/plugin-engine'\nimport type { EventEmitter } from 'node:events'\nimport { applyCookiesOverride } from './profile.service'\n\nexport class ConsentService {\n constructor(\n private consents: ConsentRepo,\n private visitors: VisitorRepo,\n private profiles: ProfileRepo,\n private audit: AuditRepo,\n private tenantId: string = 'default',\n private compliance?: ComplianceConfig,\n private pluginEngine?: PluginEngine,\n private eventBus?: EventEmitter,\n private tcfConfig?: TcfConfig,\n private storage?: StorageAdapter,\n private consentSigningKey?: string,\n ) {}\n\n private sign(payload: { tenantId: string; visitorId: string; profileId: string; locale: string; consentJson: ConsentDbRecord['consentJson'] }): string | undefined {\n if (!this.consentSigningKey) return undefined\n return signHmac(buildConsentSignaturePayload(payload), this.consentSigningKey)\n }\n\n /**\n * Profiles created via the dashboard wizard reference a consent template (`consentTemplateId`)\n * rather than embedding `cookies`/`preferenceModal.categories` directly on `profileJson` — those\n * fields are only populated for the embedded/seeded default profiles (see\n * `ProfileService.seedDefaultProfile`). Cookie/category membership and `legalBasis` are\n * locale-independent, so the raw consent template's data is sufficient here — no need to\n * resolve per-locale text via `ProfileService.getResolved()`. Mirrors the same fallback\n * `ProfileService.getResolved()` and the admin route validators already apply.\n */\n private async resolveTemplateData(pj: ProfileConfig): Promise<{ cookies: CookieMap; categories: CategoryMap }> {\n const needsCookies = !pj.cookies\n const needsCategories = !pj.preferenceModal?.categories\n const ct = (needsCookies || needsCategories) && pj.consentTemplateId && this.storage\n ? await this.storage.getConsentTemplate(pj.consentTemplateId)\n : null\n return {\n cookies: pj.cookies ?? (ct ? applyCookiesOverride(ct.cookies, pj.cookiesOverride) : {}),\n categories: pj.preferenceModal?.categories ?? ct?.categories ?? {},\n }\n }\n\n async create(input: Omit<CreateConsentInput, 'tenantId'>): Promise<ConsentDbRecord> {\n const profile = await this.profiles.get(input.profileId)\n if (!profile) throw new Error(`Profile ${input.profileId} not found`)\n\n const regulation = profile.profileJson.regulation\n const { cookies: profileCookies, categories } = await this.resolveTemplateData(profile.profileJson)\n const categoryIndex = buildCookieCategoryIndex(categories)\n // GPC server-side enforcement — skip for DPDPA (no GPC opt-out signal recognised)\n let effectiveConsent = input.consentJson\n if (input.gpcDetected && this.compliance?.gpc !== false && regulation !== 'dpdpa') {\n effectiveConsent = { ...effectiveConsent }\n for (const [id, cookie] of Object.entries(profileCookies)) {\n if (regulation === 'cpra') {\n // CPRA: deny sale + sharing on GPC; sensitive stays as submitted\n if (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing') {\n effectiveConsent[id] = 'denied'\n }\n } else if (cookie.listenGpc) {\n effectiveConsent[id] = 'denied'\n }\n }\n }\n\n const validation = validateConsentInput({ ...input, consentJson: effectiveConsent }, profileCookies, categoryIndex)\n if (!validation.valid) throw new Error(validation.errors.join('; '))\n\n const builtConsent = buildConsentValue(\n profileCookies,\n categoryIndex,\n effectiveConsent,\n input.gpcDetected,\n regulation,\n )\n\n let tcfString: string | undefined\n if (this.tcfConfig?.enabled && Object.values(profileCookies).some(c => c.tcfVendorId)) {\n const gvl = await getGvl()\n const { vendorConsents, purposeConsents } = buildTcfPayload(profileCookies, builtConsent)\n tcfString = encodeTcString({\n cmpId: this.tcfConfig.cmpId,\n cmpVersion: this.tcfConfig.cmpVersion,\n consentScreen: 1,\n consentLanguage: input.locale?.slice(0, 2) ?? 'en',\n vendorListVersion: gvl?.vendorListVersion ?? 0,\n purposeConsents,\n vendorConsents,\n })\n }\n\n const signature = this.sign({\n tenantId: this.tenantId, visitorId: input.visitorId, profileId: input.profileId,\n locale: input.locale, consentJson: builtConsent,\n })\n\n let baseInput: CreateConsentInput = {\n ...input,\n tenantId: this.tenantId,\n consentJson: builtConsent,\n ...(tcfString ? { tcfString } : {}),\n ...(signature ? { signature } : {}),\n }\n if (this.pluginEngine) baseInput = await this.pluginEngine.runBeforeConsentSave(baseInput)\n\n const existing = await this.consents.get(input.visitorId)\n let record: ConsentDbRecord\n\n if (existing) {\n const updateData = await (this.pluginEngine\n ? this.pluginEngine.runBeforeConsentUpdate({ consentJson: builtConsent, locale: input.locale, gpcDetected: input.gpcDetected, ...(signature ? { signature } : {}) })\n : Promise.resolve({ consentJson: builtConsent, locale: input.locale, gpcDetected: input.gpcDetected, ...(signature ? { signature } : {}) }))\n record = await this.consents.update(input.visitorId, updateData)\n } else {\n record = await this.consents.create(baseInput)\n }\n\n await this.audit.log({\n tenantId: this.tenantId,\n action: existing ? 'consent.updated' : 'consent.created',\n resourceType: 'consent',\n resourceId: record.id,\n ...(existing != null ? { oldData: existing } : {}),\n newData: record,\n })\n\n if (existing) {\n this.eventBus?.emit('consent.updated', { previous: existing, current: record })\n await this.pluginEngine?.runAfterConsentUpdate(record)\n } else {\n this.eventBus?.emit('consent.created', record)\n await this.pluginEngine?.runAfterConsentSave(record)\n }\n\n return record\n }\n\n async update(visitorId: string, input: UpdateConsentInput): Promise<ConsentDbRecord> {\n const existing = await this.consents.get(visitorId)\n if (!existing) throw new Error(`Consent for visitor ${visitorId} not found`)\n\n const profile = await this.profiles.get(existing.profileId)\n if (!profile) throw new Error(`Profile ${existing.profileId} not found`)\n\n const regulation = profile.profileJson.regulation\n const { cookies: profileCookies2, categories: categories2 } = await this.resolveTemplateData(profile.profileJson)\n const categoryIndex2 = buildCookieCategoryIndex(categories2)\n const effectiveGpc = input.gpcDetected ?? existing.gpcDetected\n let effectiveConsent = input.consentJson\n if (effectiveGpc && this.compliance?.gpc !== false && regulation !== 'dpdpa') {\n effectiveConsent = { ...effectiveConsent }\n for (const [id, cookie] of Object.entries(profileCookies2)) {\n if (regulation === 'cpra') {\n if (cookie.cpraCategory === 'sale' || cookie.cpraCategory === 'sharing') {\n effectiveConsent[id] = 'denied'\n }\n } else if (cookie.listenGpc) {\n effectiveConsent[id] = 'denied'\n }\n }\n }\n\n const builtConsent = buildConsentValue(\n profileCookies2,\n categoryIndex2,\n effectiveConsent,\n effectiveGpc,\n regulation,\n )\n\n const signature = this.sign({\n tenantId: this.tenantId, visitorId, profileId: existing.profileId,\n locale: input.locale ?? existing.locale, consentJson: builtConsent,\n })\n\n const updatePayload: UpdateConsentInput = {\n consentJson: builtConsent,\n ...(input.locale !== undefined ? { locale: input.locale } : {}),\n ...(input.gpcDetected !== undefined ? { gpcDetected: input.gpcDetected } : {}),\n ...(signature ? { signature } : {}),\n }\n const updateData = await (this.pluginEngine\n ? this.pluginEngine.runBeforeConsentUpdate(updatePayload)\n : Promise.resolve(updatePayload))\n const record = await this.consents.update(visitorId, updateData)\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'consent.updated',\n resourceType: 'consent',\n resourceId: record.id,\n oldData: existing,\n newData: record,\n })\n this.eventBus?.emit('consent.updated', { previous: existing, current: record })\n await this.pluginEngine?.runAfterConsentUpdate(record)\n return record\n }\n\n get(visitorId: string): Promise<ConsentDbRecord | null> {\n return this.consents.get(visitorId)\n }\n\n async verify(visitorId: string): Promise<ConsentVerifyResult> {\n const record = await this.consents.get(visitorId)\n if (!record) return { valid: false, reasons: ['profile_changed'] }\n\n const recordProfile = await this.profiles.get(record.profileId)\n if (!recordProfile) return { valid: false, reasons: ['profile_changed'] }\n\n const complianceGroup = recordProfile.profileJson.complianceGroup\n const activeProfile = complianceGroup ? await this.profiles.findActiveByComplianceGroup(this.tenantId, complianceGroup) : null\n\n return verifyConsent(record, recordProfile, activeProfile, this.consentSigningKey)\n }\n\n async erase(visitorId: string): Promise<void> {\n const existing = await this.consents.get(visitorId)\n await this.consents.delete(visitorId)\n await this.visitors.delete(visitorId)\n await this.audit.log({\n tenantId: this.tenantId,\n action: 'consent.erased',\n resourceType: 'consent',\n resourceId: visitorId,\n ...(existing != null ? { oldData: existing } : {}),\n })\n this.eventBus?.emit('consent.erased', { visitorId })\n }\n}\n","import type { Visitor, CreateVisitorInput } from '@consenti/types'\nimport { hashIp, hashUserAgent } from '../utils/crypto'\nimport type { VisitorRepo } from '../repositories/visitor.repo'\nimport type { EventEmitter } from 'node:events'\n\nexport class VisitorService {\n constructor(\n private visitors: VisitorRepo,\n private tenantId: string = 'default',\n private eventBus?: EventEmitter,\n ) {}\n\n async upsert(data: {\n visitorId: string\n ip?: string\n userAgent?: string\n country?: string\n region?: string\n city?: string\n }): Promise<Visitor> {\n const existing = await this.visitors.get(data.visitorId)\n if (existing) {\n return this.visitors.update(data.visitorId, {\n ...(data.country != null ? { country: data.country } : {}),\n ...(data.region != null ? { region: data.region } : {}),\n ...(data.city != null ? { city: data.city } : {}),\n })\n }\n const input: CreateVisitorInput = {\n tenantId: this.tenantId,\n visitorId: data.visitorId,\n ...(data.country != null ? { country: data.country } : {}),\n ...(data.region != null ? { region: data.region } : {}),\n ...(data.city != null ? { city: data.city } : {}),\n ...(data.ip ? { ipHash: hashIp(data.ip) } : {}),\n ...(data.userAgent ? { userAgentHash: hashUserAgent(data.userAgent) } : {}),\n }\n const visitor = await this.visitors.create(input)\n this.eventBus?.emit('visitor.created', visitor)\n return visitor\n }\n}\n","import type { NoticeShownRecord } from '@consenti/types'\nimport type { NoticeRepo } from '../repositories/notice.repo'\n\nexport class NoticeService {\n constructor(\n private notices: NoticeRepo,\n private tenantId: string = 'default',\n ) {}\n\n recordShown(data: { visitorId: string; profileId: string; locale: string }): Promise<NoticeShownRecord> {\n return this.notices.create({ tenantId: this.tenantId, ...data })\n }\n\n getForVisitor(visitorId: string): Promise<NoticeShownRecord[]> {\n return this.notices.getForVisitor(visitorId)\n }\n}\n","import type { SafeAdminUser, AdminUser, UpdateUserInput } from '@consenti/types'\nimport { hashPassword } from '../utils/crypto'\nimport type { UserRepo } from '../repositories/user.repo'\nimport type { AuditRepo } from '../repositories/audit.repo'\n\nexport class UserService {\n constructor(\n private users: UserRepo,\n private audit: AuditRepo,\n private tenantId: string = 'default',\n ) {}\n\n private safe(user: AdminUser): SafeAdminUser {\n const { passwordHash: _p, ...rest } = user\n return rest\n }\n\n async create(\n input: { name: string; email: string; password: string; roleId?: string; allowedTenants?: string[] },\n actorId?: string,\n ): Promise<SafeAdminUser> {\n const user = await this.users.create({\n tenantId: this.tenantId,\n name: input.name,\n email: input.email,\n passwordHash: hashPassword(input.password),\n ...(input.allowedTenants ? { allowedTenants: input.allowedTenants } : {}),\n })\n if (input.roleId) await this.users.assignRole(user.id, input.roleId)\n await this.audit.log({\n tenantId: this.tenantId,\n ...(actorId != null ? { userId: actorId } : {}),\n action: 'user.created',\n resourceType: 'user',\n resourceId: user.id,\n newData: this.safe(user),\n })\n return this.safe(user)\n }\n\n async update(\n id: string,\n input: { name?: string; email?: string; isActive?: boolean; password?: string; allowedTenants?: string[] },\n actorId?: string,\n ): Promise<SafeAdminUser> {\n const existing = await this.users.getById(id)\n if (!existing) throw new Error(`User ${id} not found`)\n const data: UpdateUserInput = {}\n if (input.name != null) data.name = input.name\n if (input.email != null) data.email = input.email\n if (input.isActive != null) data.isActive = input.isActive\n if (input.password) data.passwordHash = hashPassword(input.password)\n if (input.allowedTenants != null) data.allowedTenants = input.allowedTenants\n const updated = await this.users.update(id, data)\n await this.audit.log({\n tenantId: this.tenantId,\n ...(actorId != null ? { userId: actorId } : {}),\n action: 'user.updated',\n resourceType: 'user',\n resourceId: id,\n oldData: this.safe(existing),\n newData: this.safe(updated),\n })\n return this.safe(updated)\n }\n\n async delete(id: string, actorId?: string): Promise<void> {\n const existing = await this.users.getById(id)\n await this.users.delete(id)\n await this.audit.log({\n tenantId: this.tenantId,\n ...(actorId != null ? { userId: actorId } : {}),\n action: 'user.deleted',\n resourceType: 'user',\n resourceId: id,\n ...(existing != null ? { oldData: this.safe(existing) } : {}),\n })\n }\n\n async get(id: string): Promise<SafeAdminUser | null> {\n const user = await this.users.getById(id)\n return user ? this.safe(user) : null\n }\n\n async list(): Promise<SafeAdminUser[]> {\n const users = await this.users.list(this.tenantId)\n return users.map(u => this.safe(u))\n }\n\n getRoles(userId: string, tenantId?: string) { return this.users.getRoles(userId, tenantId) }\n assignRole(userId: string, roleId: string, tenantId?: string) { return this.users.assignRole(userId, roleId, tenantId) }\n revokeRole(userId: string, roleId: string, tenantId?: string) { return this.users.revokeRole(userId, roleId, tenantId) }\n}\n","import type { StorageAdapter, AdminUser } from '@consenti/types'\nimport { hashPassword, verifyPassword, signJwt, verifyJwt } from '../utils/crypto'\n\nconst LOCKOUT_WINDOW_MS = 15 * 60_000\nconst MAX_FAILED_ATTEMPTS = 5\n\n/** Admin session TTL — the dashboard silently refreshes the token on user activity (POST\n * /auth/refresh), so this fixed per-token lifetime is what actually implements \"expires after\n * 30 minutes of inactivity\": each refresh just extends it another 30 minutes from now. */\nconst SESSION_TTL_SECONDS = 30 * 60\n\nexport class LocalAuth {\n private loginAttempts = new Map<string, { count: number; resetAt: number }>()\n\n constructor(\n private storage: StorageAdapter,\n private secret: string,\n ) {\n setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of this.loginAttempts) {\n if (now >= entry.resetAt) this.loginAttempts.delete(key)\n }\n }, LOCKOUT_WINDOW_MS).unref()\n }\n\n async bootstrap(email: string, password: string): Promise<AdminUser | null> {\n const count = await this.storage.countUsers('default')\n if (count > 0) return null\n if (password.length < 12) {\n throw new Error('[Consenti] bootstrap: admin password must be at least 12 characters')\n }\n if (password.length < 16) {\n console.warn('[Consenti] bootstrap: admin password is shorter than 16 characters — consider using a longer password')\n }\n const passwordHash = hashPassword(password)\n const user = await this.storage.createUser({\n tenantId: 'default',\n name: 'Admin',\n email,\n passwordHash,\n })\n await this.storage.assignRole(user.id, 'role_super_admin')\n return user\n }\n\n async login(email: string, password: string): Promise<string | null> {\n const now = Date.now()\n const attempts = this.loginAttempts.get(email)\n if (attempts && now < attempts.resetAt && attempts.count >= MAX_FAILED_ATTEMPTS) return null\n\n const user = await this.storage.getUserByEmail(email)\n if (!user || !user.isActive || !verifyPassword(password, user.passwordHash)) {\n const entry = this.loginAttempts.get(email)\n if (!entry || now >= entry.resetAt) {\n this.loginAttempts.set(email, { count: 1, resetAt: now + LOCKOUT_WINDOW_MS })\n } else {\n entry.count += 1\n }\n return null\n }\n\n this.loginAttempts.delete(email)\n const roles = await this.storage.getUserRoles(user.id)\n const permSets = await Promise.all(roles.map(r => this.storage.getPermissionsForRole(r.id)))\n const permissions = [...new Set(permSets.flat().map(p => p.name))]\n return signJwt(\n {\n sub: user.id,\n email: user.email,\n roles: roles.map(r => r.name),\n permissions,\n allowedTenants: user.allowedTenants ?? [],\n },\n this.secret,\n SESSION_TTL_SECONDS,\n )\n }\n\n signToken(payload: { sub: string; email: string; roles: string[]; permissions: string[]; allowedTenants?: string[] }): string {\n return signJwt(payload, this.secret, SESSION_TTL_SECONDS)\n }\n\n verify(token: string): Record<string, unknown> | null {\n return verifyJwt(token, this.secret)\n }\n\n async getUser(token: string): Promise<AdminUser | null> {\n const payload = this.verify(token)\n if (!payload) return null\n const id = payload['sub']\n if (typeof id !== 'string') return null\n return this.storage.getUserById(id)\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport class PayloadTooLargeError extends Error {\n constructor() { super('Payload Too Large') }\n}\n\nexport function getIp(req: IncomingMessage, trustedProxies?: string[]): string {\n const remoteIp = req.socket?.remoteAddress ?? ''\n if (!trustedProxies?.length) return remoteIp\n if (!trustedProxies.includes(remoteIp)) return remoteIp\n const fwd = req.headers['x-forwarded-for']\n if (typeof fwd === 'string') return fwd.split(',')[0]?.trim() ?? remoteIp\n if (Array.isArray(fwd)) return fwd[0]?.trim() ?? remoteIp\n return remoteIp\n}\n\nexport function json(status: number, data: unknown): Response {\n return new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\nexport async function parseJsonBody<T = unknown>(request: Request): Promise<T | null> {\n const ct = request.headers.get('content-type') ?? ''\n if (!ct.includes('application/json')) return null\n try {\n return await request.json() as T\n } catch {\n return null\n }\n}\n\nexport function getQueryParam(url: URL, key: string): string | undefined {\n const val = url.searchParams.get(key)\n return val ?? undefined\n}\n\nexport function getQueryInt(url: URL, key: string, fallback: number): number {\n const val = url.searchParams.get(key)\n if (!val) return fallback\n const n = parseInt(val, 10)\n return isNaN(n) ? fallback : n\n}\n\nexport const MAX_LIST_PAGE_SIZE = 500\n\n/**\n * Reads and validates `page`/`limit` for a list endpoint. `page` is silently clamped to >= 1\n * (a stale/malformed page param shouldn't 400 a normal page load — it just re-shows page 1).\n * `limit` outside [1, MAX_LIST_PAGE_SIZE] is rejected outright: an unbounded or negative limit\n * can force a full-table scan or an invalid OFFSET at the storage layer.\n */\nexport function getPagination(url: URL, defaultLimit = 50): { page: number; limit: number } | { error: string } {\n const limit = getQueryInt(url, 'limit', defaultLimit)\n if (limit < 1 || limit > MAX_LIST_PAGE_SIZE) return { error: `limit must be between 1 and ${MAX_LIST_PAGE_SIZE}` }\n const page = Math.max(1, getQueryInt(url, 'page', 1))\n return { page, limit }\n}\n\nexport async function nodeRequestToFetch(\n req: IncomingMessage,\n maxBodyBytes = 1_048_576,\n trustedProxies?: string[],\n): Promise<Request> {\n const rawProto = req.headers['x-forwarded-proto']\n const proto = rawProto === 'https' ? 'https' : 'http'\n const host = req.headers['host'] ?? 'localhost'\n const url = new URL(req.url ?? '/', `${proto}://${host}`)\n\n const headers = new Headers()\n for (const [key, value] of Object.entries(req.headers)) {\n if (!value) continue\n if (Array.isArray(value)) {\n for (const v of value) headers.append(key, v)\n } else {\n headers.set(key, value)\n }\n }\n headers.set('x-real-ip', getIp(req, trustedProxies))\n\n let body: BodyInit | null = null\n const method = (req.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const chunks: Buffer[] = []\n let totalBytes = 0\n for await (const chunk of req) {\n totalBytes += (chunk as Buffer).length\n if (totalBytes > maxBodyBytes) throw new PayloadTooLargeError()\n chunks.push(chunk as Buffer)\n }\n if (chunks.length > 0) body = Buffer.concat(chunks)\n }\n\n return new Request(url.toString(), { method, headers, body })\n}\n\nexport async function fetchResponseToNode(response: Response, res: ServerResponse): Promise<void> {\n res.statusCode = response.status\n const setCookies: string[] =\n typeof (response.headers as { getSetCookie?: () => string[] }).getSetCookie === 'function'\n ? (response.headers as unknown as { getSetCookie: () => string[] }).getSetCookie()\n : (response.headers.get('set-cookie') ? [response.headers.get('set-cookie')!] : [])\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'set-cookie') return\n res.setHeader(key, value)\n })\n for (const cookie of setCookies) {\n res.appendHeader('set-cookie', cookie)\n }\n if (response.body) {\n const reader = response.body.getReader()\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n res.write(Buffer.from(value))\n }\n } finally {\n reader.releaseLock()\n }\n res.end()\n } else {\n res.end()\n }\n}\n","export function errorResponse(status: number, message: string, details?: unknown): Response {\n const body: Record<string, unknown> = { error: message }\n if (details != null && process.env['NODE_ENV'] !== 'production') {\n body['details'] = details\n }\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n })\n}\n\nexport async function withErrorHandler(fn: () => Promise<Response>): Promise<Response> {\n try {\n return await fn()\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Internal Server Error'\n return errorResponse(500, 'Internal Server Error',\n process.env['NODE_ENV'] !== 'production' ? message : undefined)\n }\n}\n","import type { ProfileService } from '../../services/profile.service'\nimport type { GeoResolverService } from '../../services/geo-resolver.service'\nimport { json } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nconst CACHE_CONTROL = 'public, max-age=3600, stale-while-revalidate=60'\nconst JSON_HEADERS = { 'Content-Type': 'application/json', 'Cache-Control': CACHE_CONTROL }\n\nfunction serveJsonFile(filePath: string): Response {\n const content = readFileSync(filePath, 'utf8')\n return new Response(content, { headers: JSON_HEADERS })\n}\n\nfunction redirectToDefault(basePath: string, tenantId: string, complianceGroup: string): Response {\n return new Response(null, {\n status: 303,\n headers: { Location: `${basePath}/profiles/${tenantId}/${complianceGroup}/default` },\n })\n}\n\nexport function buildProfileRoutes(\n profiles: ProfileService,\n geoResolver?: GeoResolverService,\n profilesDir?: string,\n basePath = '',\n) {\n return {\n // Static file route — primary hot-serve path, zero DB when file exists\n 'GET /profiles/:tenantId/:complianceGroupOrProfileId/:locale': async (\n _req: Request,\n params: Record<string, string>,\n ): Promise<Response> =>\n withErrorHandler(async () => {\n const { tenantId, complianceGroupOrProfileId, locale } = params as Record<string, string>\n if (!tenantId || !complianceGroupOrProfileId || !locale) return errorResponse(400, 'Missing path parameters')\n\n if (profilesDir) {\n const filePath = join(profilesDir, tenantId, complianceGroupOrProfileId, `${locale}.json`)\n if (existsSync(filePath)) return serveJsonFile(filePath)\n // Locale not found → 303 to default\n const defaultPath = join(profilesDir, tenantId, complianceGroupOrProfileId, 'default.json')\n if (existsSync(defaultPath)) return redirectToDefault(basePath, tenantId, complianceGroupOrProfileId)\n }\n\n // Fall back to DB-resolved profile (for preview of inactive/versioned profiles)\n const result = await profiles.getResolved(complianceGroupOrProfileId, locale)\n if (!result) return errorResponse(404, 'Profile not found')\n return json(200, result)\n }),\n\n // Historical snapshot file route — :profileId resolves to its lineage, :entryId picks\n // the specific edit within that lineage (see ProfileService.listVersions/getVersionFile).\n 'GET /profiles/:tenantId/:profileId/:entryId/:locale': async (\n _req: Request,\n params: Record<string, string>,\n ): Promise<Response> =>\n withErrorHandler(async () => {\n const { profileId, entryId, locale } = params as Record<string, string>\n if (!profileId || !entryId || !locale) return errorResponse(400, 'Missing path parameters')\n\n const content = await profiles.getVersionFile(profileId, entryId, locale)\n if (!content) return errorResponse(404, 'Version not found')\n return new Response(content, { headers: JSON_HEADERS })\n }),\n\n // Geo-resolve endpoint — for compliance.type='auto' widgets\n // Accepts: ?data=<base64-encoded GeoHints JSON> (preferred) OR legacy ?tz=&lang=&locale= params\n // Response: { path: string|null, complianceGroup: string, locale: string, found: boolean }\n 'GET /resolve-profile': async (request: Request, _params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (!geoResolver) return errorResponse(501, 'Geo compliance routing not configured')\n const url = new URL(request.url)\n\n const ip =\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n request.headers.get('x-real-ip') ??\n ''\n\n // Decode hints from the `data` param (widget sends base64-encoded JSON), falling\n // back to individual legacy query params for backwards compatibility.\n let timezone = url.searchParams.get('tz') ?? ''\n let language = url.searchParams.get('lang') ?? request.headers.get('accept-language') ?? ''\n let requestedLocale = url.searchParams.get('locale') ?? ''\n\n const dataParam = url.searchParams.get('data')\n if (dataParam) {\n try {\n const decoded = JSON.parse(Buffer.from(dataParam, 'base64').toString('utf8')) as {\n timezone?: string; language?: string; languages?: string[]; locale?: string\n }\n if (decoded.timezone) timezone = decoded.timezone\n if (decoded.language) language = decoded.language\n if (decoded.locale) requestedLocale = decoded.locale\n } catch {\n // ignore malformed data param — fall back to legacy params\n }\n }\n\n const geo = await geoResolver.resolve({ ip, language, timezone })\n\n if (!geo.complianceGroup) {\n return json(200, { path: null, complianceGroup: 'opt-in', locale: requestedLocale || 'en', found: false })\n }\n\n const profile = await profiles.findActiveByComplianceGroup(geo.complianceGroup)\n if (!profile) {\n return json(200, { path: null, complianceGroup: geo.complianceGroup, locale: requestedLocale || 'en', found: false })\n }\n\n // Determine best locale for this profile\n const availableLocales = profile.profileJson.locales ?? []\n let resolvedLocale = profile.defaultLocale\n if (requestedLocale && availableLocales.includes(requestedLocale)) {\n resolvedLocale = requestedLocale\n } else if (availableLocales.length > 0) {\n // Try language prefix match (e.g. 'de-AT' → 'de')\n const langPrefix = (requestedLocale || language).split('-')[0] ?? ''\n const prefixMatch = availableLocales.find(l => l === langPrefix || l.startsWith(`${langPrefix}-`))\n if (prefixMatch) resolvedLocale = prefixMatch\n }\n\n const complianceGroup = geo.complianceGroup\n const tenantId = profile.tenantId\n\n // Check if the resolved static file exists on disk\n let found = false\n let filePath = profilesDir\n ? join(profilesDir, tenantId, complianceGroup, `${resolvedLocale}.json`)\n : ''\n\n if (profilesDir && existsSync(filePath)) {\n found = true\n } else if (profilesDir) {\n // Locale file missing → try the profile's default locale\n const defaultPath = join(profilesDir, tenantId, complianceGroup, `${profile.defaultLocale}.json`)\n if (existsSync(defaultPath)) {\n resolvedLocale = profile.defaultLocale\n filePath = defaultPath\n found = true\n }\n }\n\n const servePath = found\n ? `${basePath}/profiles/${tenantId}/${complianceGroup}/${resolvedLocale}`\n : null\n\n return json(200, { path: servePath, complianceGroup, locale: resolvedLocale, found })\n }),\n\n // Legacy: single-param profile route (by ID)\n 'GET /profiles/:id': async (_request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const id = params['id']\n if (!id) return errorResponse(400, 'Missing profile id')\n const result = await profiles.getResolved(id)\n if (!result) return errorResponse(404, 'Profile not found')\n return json(200, result)\n }),\n\n 'GET /profiles/:id/:locale': async (_request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { id, locale } = params as Record<string, string>\n if (!id) return errorResponse(400, 'Missing profile id')\n const result = await profiles.getResolved(id, locale)\n if (!result) return errorResponse(404, 'Profile not found')\n return json(200, result)\n }),\n\n // Geo-resolved auto route (legacy path)\n 'GET /profiles/auto/:locale': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (!geoResolver) return errorResponse(501, 'Geo compliance routing not configured')\n const locale = params['locale'] ?? 'en'\n const url = new URL(request.url)\n const ip =\n request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ??\n request.headers.get('x-real-ip') ?? ''\n const timezone = url.searchParams.get('tz') ?? ''\n const language = request.headers.get('accept-language') ?? url.searchParams.get('lang') ?? ''\n const geo = await geoResolver.resolve({ ip, language, timezone })\n if (!geo.complianceGroup) return errorResponse(404, 'Could not resolve compliance group for this location')\n const profile = await profiles.findActiveByComplianceGroup(geo.complianceGroup)\n if (!profile) return errorResponse(404, `No active profile found for compliance group: ${geo.complianceGroup}`)\n const result = await profiles.getResolved(profile.id, locale)\n if (!result) return errorResponse(404, 'Profile not found')\n return json(200, { ...result, resolvedComplianceGroup: geo.complianceGroup })\n }),\n }\n}\n","import { signHmac, verifyHmac } from './crypto'\n\n// The widget's page origin is almost never the API's own origin (self-hosted CMP backend on\n// a different domain), so this cookie has to survive a cross-site fetch. `SameSite=None`\n// requires `Secure` per spec — browsers drop the cookie entirely otherwise — so it's only\n// safe to use once we know the response is actually served over HTTPS (production). In dev,\n// same-site-but-cross-port setups (e.g. a demo app + local API) still work fine under `Lax`,\n// since `SameSite` scopes to the registrable domain, not the port.\nfunction sameSiteAttrs(): string {\n return process.env['NODE_ENV'] === 'production' ? 'SameSite=None; Secure' : 'SameSite=Lax'\n}\n\n/**\n * Ownership cookie — proves the caller's browser is the same one that submitted a given\n * consent record, so cross-origin PUT/GET-verify/DELETE calls can be authorized without the\n * API ever seeing the widget's own `consenti_data` cookie (which is scoped to the site's\n * domain, not the API's — the browser never sends it cross-site).\n *\n * Deliberately carries no consent content, and the visitor ID appears only in the cookie\n * *name* (`consenti_{visitorId}`) — not repeated in the value. The value is just\n * `consentId.signature`, where the signature is computed over `visitorId.consentId` using a\n * server-only secret. The visitor ID is still cryptographically bound into that signature\n * (derived from the requested `:visitorId`, the same value the cookie name must match) —\n * only its plaintext echo inside the value is removed, so an attacker still can't rename one\n * visitor's cookie to another visitor's name and have it pass: the signature was computed\n * over the *original* visitorId and won't match the renamed one.\n */\nfunction ownershipToken(visitorId: string, consentId: string, secret: string): string {\n const sig = signHmac(`${visitorId}.${consentId}`, secret)\n return `${consentId}.${sig}`\n}\n\nexport function buildOwnershipSetCookie(visitorId: string, consentId: string, secret: string): string {\n const name = `consenti_${visitorId}`\n const value = ownershipToken(visitorId, consentId, secret)\n return `${name}=${value}; ${sameSiteAttrs()}; Path=/; Max-Age=31536000`\n}\n\nexport function buildExpireOwnershipCookie(visitorId: string): string {\n const name = `consenti_${visitorId}`\n return `${name}=; ${sameSiteAttrs()}; Path=/; Max-Age=0`\n}\n\n/** Verifies a `consenti_{visitorId}` cookie value against the expected visitor and secret. */\nexport function verifyOwnershipToken(token: string, visitorId: string, secret: string): boolean {\n const parts = token.split('.')\n if (parts.length !== 2) return false\n const [consentId, sig] = parts\n if (!consentId || !sig) return false\n return verifyHmac(`${visitorId}.${consentId}`, sig, secret)\n}\n","// Allowlist entries are authored as full origins (the dashboard's own placeholder/hint is\n// \"https://example.com\") — bare hostnames are also accepted for anyone who typed one directly.\nfunction patternHostname(pattern: string): string {\n if (!pattern.includes('://')) return pattern\n try {\n return new URL(pattern).hostname\n } catch {\n return pattern\n }\n}\n\nfunction matchesAllowedOrigin(origin: string, allowedOrigins: string[]): boolean {\n let hostname: string\n try {\n hostname = new URL(origin).hostname\n } catch {\n return false\n }\n return allowedOrigins.some(pattern => {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(1) // '.example.com'\n return hostname === pattern.slice(2) || hostname.endsWith(suffix)\n }\n return hostname === patternHostname(pattern)\n })\n}\n\nexport function buildCorsHeaders(origin: string | null, allowedOrigins?: string[]): Record<string, string> {\n let allowOrigin = origin ?? '*'\n if (allowedOrigins && allowedOrigins.length > 0 && origin) {\n allowOrigin = matchesAllowedOrigin(origin, allowedOrigins) ? origin : 'null'\n }\n return {\n 'Access-Control-Allow-Origin': allowOrigin,\n 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n 'Access-Control-Max-Age': '86400',\n // Required for the browser to send/accept the visitor-ownership cookie on cross-origin\n // consent requests (the widget's page origin is almost never the API's own origin). Has\n // no effect when Allow-Origin ends up '*' (no Origin header on the request) — browsers\n // reject credentialed responses with a wildcard origin regardless of this header.\n 'Access-Control-Allow-Credentials': 'true',\n }\n}\n\nexport function handleCors(request: Request): Response | null {\n if (request.method === 'OPTIONS') {\n const origin = request.headers.get('origin')\n return new Response(null, {\n status: 204,\n headers: buildCorsHeaders(origin),\n })\n }\n return null\n}\n\nexport function withCors(response: Response, request: Request): Response {\n const origin = request.headers.get('origin')\n const cors = buildCorsHeaders(origin)\n const newHeaders = new Headers(response.headers)\n for (const [k, v] of Object.entries(cors)) newHeaders.set(k, v)\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n })\n}\n\nexport function checkOriginAllowed(request: Request, allowedOrigins: string[]): boolean {\n if (allowedOrigins.length === 0) return true\n const origin = request.headers.get('origin')\n if (!origin) return true // non-browser / server-side calls always allowed\n return matchesAllowedOrigin(origin, allowedOrigins)\n}\n\nconst CSP = [\n \"default-src 'self'\",\n \"script-src 'self' https://unpkg.com\",\n \"style-src 'self' https://unpkg.com 'unsafe-inline'\",\n \"img-src 'self' data:\",\n \"font-src 'self'\",\n \"connect-src 'self'\",\n \"frame-ancestors 'none'\",\n \"base-uri 'self'\",\n \"form-action 'self'\",\n].join('; ')\n\nexport function addSecurityHeaders(res: Response): Response {\n const h = new Headers(res.headers)\n h.set('X-Content-Type-Options', 'nosniff')\n h.set('X-Frame-Options', 'DENY')\n h.set('Referrer-Policy', 'no-referrer')\n h.set('Permissions-Policy', 'interest-cohort=()')\n h.set('Content-Security-Policy', CSP)\n // Browsers only honour HSTS over HTTPS; sending it over HTTP is harmless.\n h.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload')\n return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h })\n}\n","import type { CreateConsentInput, UpdateConsentInput, ConsentValue } from '@consenti/types'\n\nexport interface ValidationResult {\n valid: boolean\n error?: string\n}\n\nconst VALID_STATUSES = new Set(['granted', 'denied', 'objected'])\n\nfunction validateConsentJsonValues(consentJson: unknown): ValidationResult {\n const entries = Object.entries(consentJson as Record<string, unknown>)\n for (const [key, val] of entries) {\n if (!key) return { valid: false, error: 'consentJson keys must be non-empty strings' }\n if (!VALID_STATUSES.has(val as string)) {\n return { valid: false, error: `consentJson value \"${String(val)}\" is invalid; must be granted, denied, or objected` }\n }\n }\n return { valid: true }\n}\n\nexport function validateCreateConsent(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n const b = body as Record<string, unknown>\n if (typeof b['profileId'] !== 'string' || !b['profileId']) return { valid: false, error: 'profileId is required' }\n if (!b['consentJson'] || typeof b['consentJson'] !== 'object' || Array.isArray(b['consentJson'])) {\n return { valid: false, error: 'consentJson must be an object' }\n }\n return validateConsentJsonValues(b['consentJson'])\n}\n\nexport function validateUpdateConsent(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n const b = body as Record<string, unknown>\n if (!b['consentJson'] || typeof b['consentJson'] !== 'object' || Array.isArray(b['consentJson'])) {\n return { valid: false, error: 'consentJson must be an object' }\n }\n return validateConsentJsonValues(b['consentJson'])\n}\n\nexport function castCreateConsent(body: Record<string, unknown>, visitorId: string, tenantId: string): CreateConsentInput {\n return {\n tenantId,\n visitorId,\n profileId: body['profileId'] as string,\n locale: typeof body['locale'] === 'string' ? body['locale'] : 'en',\n consentJson: body['consentJson'] as ConsentValue,\n gpcDetected: Boolean(body['gpcDetected']),\n source: (body['source'] as CreateConsentInput['source']) ?? 'banner',\n ...(typeof body['ageVerified'] === 'boolean' ? { ageVerified: body['ageVerified'] } : {}),\n ...(typeof body['parentalConsentToken'] === 'string' ? { parentalConsentToken: body['parentalConsentToken'] } : {}),\n }\n}\n\nexport function castUpdateConsent(body: Record<string, unknown>): UpdateConsentInput {\n const result: UpdateConsentInput = {\n consentJson: body['consentJson'] as ConsentValue,\n }\n if (typeof body['locale'] === 'string') result.locale = body['locale']\n if (body['gpcDetected'] != null) result.gpcDetected = Boolean(body['gpcDetected'])\n return result\n}\n","import type { ConsentService } from '../../services/consent.service'\nimport type { VisitorService } from '../../services/visitor.service'\nimport type { ProfileService } from '../../services/profile.service'\nimport type { StorageAdapter } from '@consenti/types'\nimport { parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { buildOwnershipSetCookie, buildExpireOwnershipCookie, verifyOwnershipToken } from '../../utils/cookie'\nimport { checkOriginAllowed } from '../../middleware/cors.middleware'\nimport {\n validateCreateConsent, validateUpdateConsent,\n castCreateConsent, castUpdateConsent,\n} from '../../validators/consent.validator'\nimport { randomVisitorId } from '../../utils/crypto'\n\n// Verify that the request's cookie contains a valid consenti_<visitorId> token,\n// proving the caller is the actual visitor who originally submitted consent.\nfunction assertVisitorOwnership(request: Request, visitorId: string, secret: string): Response | null {\n const cookieHeader = request.headers.get('cookie') ?? ''\n const name = `consenti_${visitorId}`\n const owned = cookieHeader.split(';').some(c => {\n const trimmed = c.trim()\n const eqIdx = trimmed.indexOf('=')\n if (eqIdx === -1 || trimmed.slice(0, eqIdx) !== name) return false\n return verifyOwnershipToken(trimmed.slice(eqIdx + 1), visitorId, secret)\n })\n if (!owned) return errorResponse(403, 'Forbidden')\n return null\n}\n\nexport function buildConsentRoutes(\n consents: ConsentService,\n visitors: VisitorService,\n profiles: ProfileService,\n resolveTenantId: (req: Request) => Promise<string> = async () => 'default',\n storage?: StorageAdapter,\n ownershipSecret = '',\n) {\n return {\n 'POST /consent': async (request: Request, _params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const body = await parseJsonBody(request)\n const validation = validateCreateConsent(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid request')\n\n const b = body as Record<string, unknown>\n const profileId = typeof b['profileId'] === 'string' ? b['profileId'] : null\n if (!profileId) return errorResponse(400, 'profileId is required')\n\n // Layer 1: verify the profileId is real — blocks all requests with unknown profiles\n const profile = await profiles.get(profileId)\n if (!profile) return errorResponse(404, 'Profile not found')\n\n const tenantId = await resolveTenantId(request)\n\n // Layer 3: enforce the profile's allowed-domains list, falling back to the tenant-wide\n // default (dashboard's API Config → Allowed Origins) when the profile doesn't set its own.\n const pjConfig = profile.profileJson as { allowedOrigins?: string[] }\n let allowedOrigins = pjConfig.allowedOrigins ?? []\n if (allowedOrigins.length === 0 && storage) {\n allowedOrigins = (await storage.getSettings(tenantId)).allowedOrigins ?? []\n }\n if (!checkOriginAllowed(request, allowedOrigins)) {\n return errorResponse(403, 'Origin not allowed for this profile')\n }\n\n const visitorId = typeof b['visitorId'] === 'string' ? b['visitorId'] : randomVisitorId()\n const ip = request.headers.get('x-real-ip') ?? ''\n const ua = request.headers.get('user-agent') ?? ''\n\n await visitors.upsert({ visitorId, ip, userAgent: ua })\n const input = castCreateConsent(b, visitorId, tenantId)\n const record = await consents.create(input)\n\n const setCookie = buildOwnershipSetCookie(visitorId, record.id, ownershipSecret)\n return new Response(JSON.stringify(record), {\n status: 201,\n headers: {\n 'Content-Type': 'application/json',\n 'Set-Cookie': setCookie,\n },\n })\n }),\n\n 'PUT /consent/:visitorId': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: only the visitor's own browser may update their consent\n const ownershipError = assertVisitorOwnership(request, visitorId, ownershipSecret)\n if (ownershipError) return ownershipError\n\n const body = await parseJsonBody(request)\n const validation = validateUpdateConsent(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid request')\n\n const input = castUpdateConsent(body as Record<string, unknown>)\n const record = await consents.update(visitorId, input)\n\n const setCookie = buildOwnershipSetCookie(visitorId, record.id, ownershipSecret)\n return new Response(JSON.stringify(record), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Set-Cookie': setCookie,\n },\n })\n }),\n\n 'GET /consent/:visitorId': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: only the visitor's own browser may read their consent\n const ownershipError = assertVisitorOwnership(request, visitorId, ownershipSecret)\n if (ownershipError) return ownershipError\n\n const record = await consents.get(visitorId)\n if (!record) return errorResponse(404, 'Consent not found')\n return new Response(JSON.stringify(record), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n }),\n\n 'GET /consent/:visitorId/verify': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: cookie ownership (browser calls); server-side callers use admin auth upstream\n const ownershipError = assertVisitorOwnership(request, visitorId, ownershipSecret)\n if (ownershipError) return ownershipError\n\n const result = await consents.verify(visitorId)\n return new Response(JSON.stringify(result), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n })\n }),\n\n 'DELETE /consent/:visitorId': async (request: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const visitorId = params['visitorId']\n if (!visitorId) return errorResponse(400, 'Missing visitorId')\n\n // Layer 2: only the visitor's own browser may erase their consent\n const ownershipError = assertVisitorOwnership(request, visitorId, ownershipSecret)\n if (ownershipError) return ownershipError\n\n const existing = await consents.get(visitorId)\n await consents.erase(visitorId)\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (existing) {\n headers['Set-Cookie'] = buildExpireOwnershipCookie(visitorId)\n }\n return new Response(JSON.stringify({ success: true }), { status: 200, headers })\n }),\n }\n}\n","import type { NoticeService } from '../../services/notice.service'\nimport { parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\n\n/**\n * Proof-of-notice (recording that the banner was rendered to a visitor, independent of any\n * decision) is not wired up anywhere — the widget never mints a visitor identifier before a\n * consent decision exists, so it has nothing to send here. Route kept dormant, not deleted,\n * as a starting point if this is ever revisited; see /docs/upcoming-features.\n */\nconst NOTICE_SHOWN_ENABLED: boolean = false\n\nexport function buildNoticeRoutes(notices: NoticeService) {\n return {\n 'POST /notice-shown': async (request: Request, _params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (!NOTICE_SHOWN_ENABLED) return errorResponse(404, 'Not found')\n\n const body = await parseJsonBody(request)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Request body must be a JSON object')\n const b = body as Record<string, unknown>\n\n const visitorId = typeof b['visitorId'] === 'string' ? b['visitorId'] : ''\n const profileId = typeof b['profileId'] === 'string' ? b['profileId'] : ''\n const locale = typeof b['locale'] === 'string' ? b['locale'] : ''\n if (!visitorId) return errorResponse(400, 'visitorId is required')\n if (!profileId) return errorResponse(400, 'profileId is required')\n if (!locale) return errorResponse(400, 'locale is required')\n\n const record = await notices.recordShown({ visitorId, profileId, locale })\n return new Response(JSON.stringify({ success: true, id: record.id }), {\n status: 201,\n headers: { 'Content-Type': 'application/json' },\n })\n }),\n }\n}\n","import { randomBytes } from 'node:crypto'\nimport type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate } from '../../middleware/auth.middleware'\nimport type { RateLimiter } from '../../middleware/rate-limit.middleware'\nimport type { LocalAuth } from '../../auth/local.auth'\nimport {\n generatePkce, discoverOidcEndpoints, buildAuthorizationUrl,\n exchangeOidcCode, verifyOidcIdToken, extractEmailFromClaims,\n extractRolesFromClaims, storePkceState, consumePkceState,\n} from '../../auth/oidc.auth'\nimport { buildSamlMetadataXml, validateSamlResponse } from '../../auth/saml.auth'\nimport { generateTotpSecret, verifyTotp, totpQrUrl } from '../../auth/totp'\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n}\n\nexport function buildAdminAuthRoutes(\n localAuth: LocalAuth,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n authLimiter?: RateLimiter | null,\n) {\n function checkAuthLimit(req: Request): Response | null {\n if (!authLimiter) return null\n const ip = req.headers.get('x-real-ip') ?? ''\n if (!authLimiter.checkRequest(ip)) return errorResponse(429, 'Too Many Requests')\n return null\n }\n\n return {\n 'POST /auth/login': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const limited = checkAuthLimit(req)\n if (limited) return limited\n if (authConfig.mode !== 'local') return errorResponse(400, 'Login not available in this auth mode')\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['email'] !== 'string' || typeof b['password'] !== 'string') {\n return errorResponse(400, 'email and password are required')\n }\n const token = await localAuth.login(b['email'], b['password'])\n if (!token) return errorResponse(401, 'Invalid credentials')\n return json(200, { token })\n }),\n\n 'GET /auth/me': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n const roles = await storage.getUserRoles(dbUser.id)\n return json(200, {\n id: dbUser.id,\n email: dbUser.email,\n name: dbUser.name,\n isActive: dbUser.isActive,\n totpEnabled: dbUser.totpEnabled ?? false,\n roles: roles.map(r => r.name),\n permissions: user.permissions,\n })\n }),\n\n 'POST /auth/logout': async (_req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => json(200, { success: true })),\n\n /** Reissues a fresh token from the current (still-valid) one, extending the session another\n * `SESSION_TTL_SECONDS` — called by the dashboard on user activity to implement a sliding\n * inactivity timeout rather than a flat expiry from login. 401s the same as any other\n * authenticated route if the current token is already invalid/expired — by design, since the\n * dashboard only ever calls this proactively, before expiry. */\n 'POST /auth/refresh': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const token = localAuth.signToken({\n sub: user.sub,\n email: user.email,\n roles: user.roles,\n permissions: user.permissions,\n allowedTenants: user.allowedTenants,\n })\n return json(200, { token })\n }),\n\n // ── OIDC ────────────────────────────────────────────────────────────────────\n\n 'GET /auth/oidc/authorize': async (_req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (authConfig.mode !== 'oidc' || !authConfig.oidc) return errorResponse(400, 'OIDC not configured')\n const endpoints = await discoverOidcEndpoints(authConfig.oidc.issuer)\n const state = base64url(randomBytes(16))\n const nonce = base64url(randomBytes(16))\n const { verifier, challenge } = generatePkce()\n storePkceState(state, verifier, nonce)\n const url = buildAuthorizationUrl(authConfig.oidc, endpoints.authorization_endpoint, state, nonce, challenge)\n return new Response(null, { status: 302, headers: { Location: url } })\n }),\n\n 'GET /auth/oidc/callback': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (authConfig.mode !== 'oidc' || !authConfig.oidc) return errorResponse(400, 'OIDC not configured')\n const url = new URL(req.url)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n if (!code || !state) return errorResponse(400, 'Missing code or state')\n const pkce = consumePkceState(state)\n if (!pkce) return errorResponse(400, 'Invalid or expired state')\n const endpoints = await discoverOidcEndpoints(authConfig.oidc.issuer)\n const tokens = await exchangeOidcCode(authConfig.oidc, endpoints.token_endpoint, code, pkce.verifier)\n const claims = await verifyOidcIdToken(tokens.id_token, endpoints.jwks_uri)\n const email = extractEmailFromClaims(claims, authConfig.oidc.claimsMapping)\n const roles = extractRolesFromClaims(claims, authConfig.oidc.claimsMapping)\n // Upsert admin user from OIDC claims\n let user = await storage.getUserByEmail(email)\n if (!user) {\n user = await storage.createUser({ tenantId: 'default', name: email, email, passwordHash: '' })\n }\n if (roles.length > 0) {\n const allRoles = await storage.getRoles('default')\n for (const roleName of roles) {\n const role = allRoles.find(r => r.name === roleName)\n if (role) await storage.assignRole(user.id, role.id).catch(() => {})\n }\n }\n const userRoles = await storage.getUserRoles(user.id)\n const permissions: string[] = []\n for (const role of userRoles) {\n const perms = await storage.getPermissionsForRole(role.id)\n permissions.push(...perms.map(p => p.name))\n }\n const token = localAuth.signToken({\n sub: user.id, email: user.email,\n roles: userRoles.map(r => r.name),\n permissions: [...new Set(permissions)],\n })\n return json(200, { token })\n }),\n\n // ── SAML ────────────────────────────────────────────────────────────────────\n\n 'GET /auth/saml/metadata': async (_req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (authConfig.mode !== 'saml' || !authConfig.saml) return errorResponse(400, 'SAML not configured')\n const xml = buildSamlMetadataXml(authConfig.saml)\n return new Response(xml, { status: 200, headers: { 'Content-Type': 'application/xml' } })\n }),\n\n 'POST /auth/saml/acs': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const limited = checkAuthLimit(req)\n if (limited) return limited\n if (authConfig.mode !== 'saml' || !authConfig.saml) return errorResponse(400, 'SAML not configured')\n const params = new URLSearchParams(await req.text())\n const samlResponse = params.get('SAMLResponse')\n if (typeof samlResponse !== 'string') return errorResponse(400, 'Missing SAMLResponse')\n const info = await validateSamlResponse(samlResponse, authConfig.saml)\n if (!info) return errorResponse(401, 'SAML validation failed')\n let user = await storage.getUserByEmail(info.email)\n if (!user) {\n user = await storage.createUser({ tenantId: 'default', name: info.email, email: info.email, passwordHash: '' })\n }\n const userRoles = await storage.getUserRoles(user.id)\n const permissions: string[] = []\n for (const role of userRoles) {\n const perms = await storage.getPermissionsForRole(role.id)\n permissions.push(...perms.map(p => p.name))\n }\n const token = localAuth.signToken({\n sub: user.id, email: user.email,\n roles: userRoles.map(r => r.name),\n permissions: [...new Set(permissions)],\n })\n return json(200, { token })\n }),\n\n // ── TOTP ────────────────────────────────────────────────────────────────────\n\n 'POST /auth/totp/setup': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n const totpSecret = generateTotpSecret()\n const qrUrl = totpQrUrl(totpSecret, dbUser.email)\n await storage.updateUser(dbUser.id, { totpSecret, totpEnabled: false })\n return json(200, { qrUrl, message: 'Scan QR code, then verify with POST /auth/totp/verify' })\n }),\n\n 'POST /auth/totp/verify': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const limited = checkAuthLimit(req)\n if (limited) return limited\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n if (!dbUser.totpSecret) return errorResponse(400, 'TOTP not set up — call POST /auth/totp/setup first')\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['token'] !== 'string') return errorResponse(400, 'token is required')\n if (!verifyTotp(dbUser.totpSecret, b['token'])) return errorResponse(400, 'Invalid TOTP token')\n await storage.updateUser(dbUser.id, { totpEnabled: true })\n return json(200, { success: true, message: 'TOTP enabled successfully' })\n }),\n\n 'POST /auth/totp/disable': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const user = await authenticate(req, storage, authConfig, secret)\n if (!user) return errorResponse(401, 'Unauthorized')\n const dbUser = await storage.getUserById(user.sub)\n if (!dbUser) return errorResponse(401, 'User not found')\n await storage.updateUser(dbUser.id, { totpSecret: null, totpEnabled: false })\n return json(200, { success: true, message: 'TOTP disabled' })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { verifyJwt } from '../utils/crypto'\nimport { errorResponse } from './error.middleware'\n\nexport interface AuthenticatedUser {\n sub: string\n email: string\n roles: string[]\n permissions: string[]\n allowedTenants: string[] // empty = all tenants\n}\n\nexport async function authenticate(\n req: Request,\n storage: StorageAdapter,\n config: AuthConfig,\n secret: string,\n): Promise<AuthenticatedUser | null> {\n const auth = req.headers.get('authorization')\n const token = auth?.startsWith('Bearer ') ? auth.slice(7) : null\n if (!token) return null\n\n if (config.mode === 'local' || config.mode === 'jwt') {\n const payload = verifyJwt(token, secret)\n if (!payload) return null\n const sub = payload['sub']\n const email = payload['email']\n const roles = payload['roles']\n const permissions = payload['permissions']\n const allowedTenants = payload['allowedTenants']\n if (typeof sub !== 'string' || typeof email !== 'string') return null\n return {\n sub,\n email,\n roles: Array.isArray(roles) ? (roles as string[]) : [],\n permissions: Array.isArray(permissions) ? (permissions as string[]) : [],\n allowedTenants: Array.isArray(allowedTenants) ? (allowedTenants as string[]) : [],\n }\n }\n\n if (config.mode === 'custom' && config.validateUser) {\n const user = await config.validateUser(req)\n if (!user) return null\n const roles = await storage.getUserRoles(user.id)\n const permSets = await Promise.all(roles.map(r => storage.getPermissionsForRole(r.id)))\n const permissions = [...new Set(permSets.flat().map(p => p.name))]\n return {\n sub: user.id,\n email: user.email,\n roles: roles.map(r => r.name),\n permissions,\n allowedTenants: [],\n }\n }\n\n return null\n}\n\nexport function authError(\n user: AuthenticatedUser | null,\n permission?: string,\n): Response | null {\n if (!user) return errorResponse(401, 'Unauthorized')\n if (permission && !user.permissions.includes(permission)) return errorResponse(403, 'Forbidden')\n return null\n}\n","import { randomBytes, createHash } from 'node:crypto'\nimport type { OidcConfig } from '@consenti/types'\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '')\n}\n\nfunction base64urlDecode(s: string): string {\n return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8')\n}\n\nexport interface PkceChallenge {\n verifier: string\n challenge: string\n}\n\nexport function generatePkce(): PkceChallenge {\n const verifier = base64url(randomBytes(32))\n const challenge = base64url(createHash('sha256').update(verifier).digest())\n return { verifier, challenge }\n}\n\ninterface OidcEndpoints {\n authorization_endpoint: string\n token_endpoint: string\n userinfo_endpoint?: string\n jwks_uri: string\n}\n\nexport async function discoverOidcEndpoints(issuer: string): Promise<OidcEndpoints> {\n const res = await fetch(`${issuer}/.well-known/openid-configuration`, {\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) throw new Error(`OIDC discovery failed for ${issuer}: ${res.status}`)\n return res.json() as Promise<OidcEndpoints>\n}\n\nexport function buildAuthorizationUrl(\n config: OidcConfig,\n authorizationEndpoint: string,\n state: string,\n nonce: string,\n codeChallenge: string,\n): string {\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: config.clientId,\n redirect_uri: config.redirectUri,\n scope: 'openid email profile',\n state,\n nonce,\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n })\n return `${authorizationEndpoint}?${params.toString()}`\n}\n\ninterface TokenResponse {\n id_token: string\n access_token: string\n token_type: string\n expires_in?: number\n}\n\nexport async function exchangeOidcCode(\n config: OidcConfig,\n tokenEndpoint: string,\n code: string,\n codeVerifier: string,\n): Promise<TokenResponse> {\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: config.clientId,\n client_secret: config.clientSecret,\n redirect_uri: config.redirectUri,\n code,\n code_verifier: codeVerifier,\n })\n const res = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n signal: AbortSignal.timeout(10_000),\n })\n if (!res.ok) {\n const err = await res.text()\n throw new Error(`OIDC token exchange failed: ${err}`)\n }\n return res.json() as Promise<TokenResponse>\n}\n\nexport interface OidcClaims {\n sub: string\n email?: string\n name?: string\n [key: string]: unknown\n}\n\nexport async function verifyOidcIdToken(idToken: string, jwksUri: string): Promise<OidcClaims> {\n const parts = idToken.split('.')\n if (parts.length !== 3) throw new Error('Invalid ID token format')\n\n const header = JSON.parse(base64urlDecode(parts[0]!)) as { kid?: string; alg?: string }\n const alg = header.alg ?? 'RS256'\n\n const res = await fetch(jwksUri, { signal: AbortSignal.timeout(10_000) })\n if (!res.ok) throw new Error(`Failed to fetch JWKS: ${res.status}`)\n type JwkEntry = JsonWebKey & { kid?: string; use?: string }\n const jwks = await res.json() as { keys: JwkEntry[] }\n\n const jwk: JwkEntry | undefined = header.kid\n ? jwks.keys.find(k => k.kid === header.kid)\n : (jwks.keys.find(k => k.use === 'sig') ?? jwks.keys[0])\n if (!jwk) throw new Error('No matching key in JWKS')\n\n let cryptoKey: CryptoKey\n if (alg === 'RS256' || alg === 'RS384' || alg === 'RS512') {\n const hashName = alg === 'RS256' ? 'SHA-256' : alg === 'RS384' ? 'SHA-384' : 'SHA-512'\n cryptoKey = await globalThis.crypto.subtle.importKey(\n 'jwk', jwk, { name: 'RSASSA-PKCS1-v1_5', hash: { name: hashName } }, false, ['verify'],\n )\n } else if (alg === 'ES256' || alg === 'ES384' || alg === 'ES512') {\n const namedCurve = alg === 'ES256' ? 'P-256' : alg === 'ES384' ? 'P-384' : 'P-521'\n cryptoKey = await globalThis.crypto.subtle.importKey(\n 'jwk', jwk, { name: 'ECDSA', namedCurve }, false, ['verify'],\n )\n } else {\n throw new Error(`Unsupported OIDC token algorithm: ${alg}`)\n }\n\n const sigInput = new TextEncoder().encode(`${parts[0]}.${parts[1]}`)\n const padding = (4 - (parts[2]!.length % 4)) % 4\n const sigB64 = parts[2]!.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat(padding)\n const sigBytes = Uint8Array.from(atob(sigB64), c => c.charCodeAt(0))\n\n const verifyAlg = alg.startsWith('ES')\n ? { name: 'ECDSA', hash: { name: `SHA-${alg.slice(2)}` } }\n : { name: 'RSASSA-PKCS1-v1_5' }\n\n const valid = await globalThis.crypto.subtle.verify(verifyAlg, cryptoKey, sigBytes, sigInput)\n if (!valid) throw new Error('ID token signature verification failed')\n\n const payload = JSON.parse(base64urlDecode(parts[1]!)) as OidcClaims\n const now = Math.floor(Date.now() / 1000)\n if (typeof payload['exp'] === 'number' && payload['exp'] < now) {\n throw new Error('ID token has expired')\n }\n\n return payload\n}\n\nexport function extractEmailFromClaims(claims: OidcClaims, mapping?: { email?: string }): string {\n const key = mapping?.email ?? 'email'\n const value = claims[key]\n if (typeof value !== 'string') throw new Error('OIDC token missing email claim')\n return value\n}\n\nexport function extractRolesFromClaims(\n claims: OidcClaims,\n mapping?: { roles?: string },\n): string[] {\n const key = mapping?.roles ?? 'consenti_roles'\n const value = claims[key]\n if (!Array.isArray(value)) return []\n return value.filter((v): v is string => typeof v === 'string')\n}\n\n// In-memory PKCE/state store. Replace with Redis or DB for multi-instance deployments.\nconst pendingStates = new Map<string, { verifier: string; nonce: string; expiresAt: number }>()\n\nsetInterval(() => {\n const now = Date.now()\n for (const [key, val] of pendingStates) {\n if (val.expiresAt < now) pendingStates.delete(key)\n }\n}, 5 * 60_000).unref()\n\nexport function storePkceState(state: string, verifier: string, nonce: string): void {\n pendingStates.set(state, { verifier, nonce, expiresAt: Date.now() + 10 * 60_000 })\n}\n\nexport function consumePkceState(\n state: string,\n): { verifier: string; nonce: string } | null {\n const entry = pendingStates.get(state)\n pendingStates.delete(state)\n if (!entry || entry.expiresAt < Date.now()) return null\n return { verifier: entry.verifier, nonce: entry.nonce }\n}\n","import type { SamlConfig } from '@consenti/types'\n\nfunction escXml(s: string): string {\n return s.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n}\n\nexport function buildSamlMetadataXml(config: SamlConfig): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n entityID=\"saml:sp:consenti\">\n <SPSSODescriptor\n protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"\n AuthnRequestsSigned=\"false\"\n WantAssertionsSigned=\"true\">\n <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>\n <AssertionConsumerService\n Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n Location=\"${escXml(config.callbackUrl)}\"\n index=\"0\"\n isDefault=\"true\"/>\n </SPSSODescriptor>\n</EntityDescriptor>`\n}\n\nexport interface SamlUserInfo {\n email: string\n roles: string[]\n}\n\n// Validates a SAML response using the `samlify` peer dependency.\n// Install: npm install samlify\nexport async function validateSamlResponse(\n samlResponse: string,\n config: SamlConfig,\n): Promise<SamlUserInfo | null> {\n let samlify: {\n ServiceProvider: (cfg: Record<string, unknown>) => {\n parseLoginResponse: (\n idp: unknown,\n type: string,\n req: { body: Record<string, string> },\n ) => Promise<{ extract: { nameID: string; attributes: Record<string, unknown> } }>\n }\n IdentityProvider: (cfg: Record<string, unknown>) => unknown\n }\n\n try {\n // @ts-expect-error -- samlify is an optional peer dependency; callers must install it\n samlify = (await import('samlify')) as unknown as typeof samlify\n } catch {\n console.warn('[Consenti] samlify is not installed. Install it to use SAML auth mode.')\n return null\n }\n\n try {\n const sp = samlify.ServiceProvider({\n entityID: 'saml:sp:consenti',\n assertionConsumerService: [\n {\n Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',\n Location: config.callbackUrl,\n },\n ],\n })\n const idp = samlify.IdentityProvider({\n entityID: config.issuer,\n singleSignOnService: [\n {\n Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',\n Location: config.entryPoint,\n },\n ],\n signingCert: config.cert,\n })\n const result = await sp.parseLoginResponse(idp, 'post', {\n body: { SAMLResponse: samlResponse },\n })\n const email = result.extract.nameID\n const rolesAttr = result.extract.attributes['roles']\n const roles = Array.isArray(rolesAttr)\n ? rolesAttr.filter((r): r is string => typeof r === 'string')\n : []\n return { email, roles }\n } catch (err) {\n console.warn('[Consenti] SAML response validation failed:', err)\n return null\n }\n}\n","import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto'\n\nconst BASE32_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'\n\nfunction base32Encode(buf: Buffer): string {\n let result = ''\n let bits = 0\n let value = 0\n for (const byte of buf) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n result += BASE32_CHARS[(value >>> (bits - 5)) & 0x1f]\n bits -= 5\n }\n }\n if (bits > 0) result += BASE32_CHARS[(value << (5 - bits)) & 0x1f]\n return result\n}\n\nfunction base32Decode(str: string): Buffer {\n const clean = str.toUpperCase().replace(/[^A-Z2-7]/g, '')\n const bytes: number[] = []\n let bits = 0\n let value = 0\n for (const char of clean) {\n const idx = BASE32_CHARS.indexOf(char)\n if (idx < 0) continue\n value = (value << 5) | idx\n bits += 5\n if (bits >= 8) {\n bytes.push((value >>> (bits - 8)) & 0xff)\n bits -= 8\n }\n }\n return Buffer.from(bytes)\n}\n\nfunction hotp(secret: Buffer, counter: number): string {\n const buf = Buffer.alloc(8)\n let c = counter\n for (let i = 7; i >= 0; i--) {\n buf[i] = c & 0xff\n c = Math.floor(c / 256)\n }\n const hmac = createHmac('sha1', secret).update(buf).digest()\n const offset = hmac[hmac.length - 1]! & 0x0f\n const code =\n ((hmac[offset]! & 0x7f) << 24) |\n ((hmac[offset + 1]! & 0xff) << 16) |\n ((hmac[offset + 2]! & 0xff) << 8) |\n (hmac[offset + 3]! & 0xff)\n return String(code % 1_000_000).padStart(6, '0')\n}\n\nexport function generateTotpSecret(): string {\n return base32Encode(randomBytes(20))\n}\n\nexport function generateTotp(secret: string, time = Date.now()): string {\n return hotp(base32Decode(secret), Math.floor(time / 30_000))\n}\n\nconst usedTokens = new Map<string, number>() // token → expiresAt\n\nsetInterval(() => {\n const now = Date.now()\n for (const [token, expiresAt] of usedTokens) {\n if (now >= expiresAt) usedTokens.delete(token)\n }\n}, 90_000).unref()\n\nexport function verifyTotp(secret: string, token: string, window = 1): boolean {\n if (usedTokens.has(token)) return false\n const counter = Math.floor(Date.now() / 30_000)\n const secretBuf = base32Decode(secret)\n const tokenBuf = Buffer.from(token)\n for (let i = -window; i <= window; i++) {\n const expected = Buffer.from(hotp(secretBuf, counter + i))\n if (expected.length === tokenBuf.length && timingSafeEqual(expected, tokenBuf)) {\n usedTokens.set(token, Date.now() + 90_000)\n return true\n }\n }\n return false\n}\n\nexport function totpQrUrl(secret: string, email: string, issuer = 'Consenti'): string {\n const label = encodeURIComponent(`${issuer}:${email}`)\n const params = new URLSearchParams({\n secret,\n issuer,\n algorithm: 'SHA1',\n digits: '6',\n period: '30',\n })\n return `otpauth://totp/${label}?${params.toString()}`\n}\n","import type { CreateProfileInput, UpdateProfileInput } from '@consenti/types'\n\nexport interface ValidationResult {\n valid: boolean\n error?: string\n}\n\nexport function validateCreateProfile(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || !b['name']) return { valid: false, error: 'name is required' }\n if (typeof b['defaultLocale'] !== 'string' || !b['defaultLocale']) return { valid: false, error: 'defaultLocale is required' }\n if (!b['profileJson'] || typeof b['profileJson'] !== 'object') return { valid: false, error: 'profileJson is required' }\n const pj = b['profileJson'] as Record<string, unknown>\n if (typeof pj['consentTemplateId'] !== 'string' || !pj['consentTemplateId']) {\n return { valid: false, error: 'profileJson.consentTemplateId is required' }\n }\n if (typeof pj['uiTemplateId'] !== 'string' || !pj['uiTemplateId']) {\n return { valid: false, error: 'profileJson.uiTemplateId is required' }\n }\n return { valid: true }\n}\n\nexport function validateUpdateProfile(body: unknown): ValidationResult {\n if (!body || typeof body !== 'object') return { valid: false, error: 'Request body must be a JSON object' }\n return { valid: true }\n}\n\nexport function castCreateProfile(body: Record<string, unknown>, tenantId: string): CreateProfileInput {\n return {\n tenantId,\n name: body['name'] as string,\n defaultLocale: body['defaultLocale'] as string,\n profileJson: body['profileJson'] as CreateProfileInput['profileJson'],\n ...(body['localeContent'] != null ? { localeContent: body['localeContent'] as NonNullable<CreateProfileInput['localeContent']> } : {}),\n }\n}\n\nexport function castUpdateProfile(body: Record<string, unknown>): UpdateProfileInput {\n const result: UpdateProfileInput = {}\n if (typeof body['name'] === 'string') result.name = body['name']\n if (typeof body['defaultLocale'] === 'string') result.defaultLocale = body['defaultLocale']\n if (body['profileJson'] != null) result.profileJson = body['profileJson'] as NonNullable<UpdateProfileInput['profileJson']>\n if (body['localeContent'] != null) result.localeContent = body['localeContent'] as NonNullable<UpdateProfileInput['localeContent']>\n return result\n}\n","import type { CookieMap, CategoryMap, ComplianceValidationResult, ComplianceViolation, ComplianceWarningItem } from '@consenti/types'\nimport { COMPLIANCE_VALIDATION_RULES, buildCookieCategoryIndex, getCookieLegalBasis } from '@consenti/utils'\n\nexport function validateProfileCompliance(\n cookies: CookieMap,\n categories: CategoryMap,\n complianceGroup: string,\n): ComplianceValidationResult {\n const rules = COMPLIANCE_VALIDATION_RULES[complianceGroup]\n if (!rules) return { valid: true, errors: [], warnings: [] }\n\n const errors: ComplianceViolation[] = []\n const warnings: ComplianceWarningItem[] = []\n const categoryIndex = buildCookieCategoryIndex(categories)\n\n for (const [categoryId, category] of Object.entries(categories)) {\n const c = category as unknown as Record<string, unknown>\n\n for (const rule of rules.categoryErrors) {\n if (rule.check(c, undefined)) {\n errors.push({\n cookieId: categoryId,\n field: rule.field,\n message: rule.message(categoryId),\n rule: rule.rule,\n })\n }\n }\n\n for (const rule of rules.categoryWarnings) {\n if (rule.check(c, undefined)) {\n warnings.push({\n cookieId: categoryId,\n field: rule.field,\n message: rule.message(categoryId),\n suggestion: rule.suggestion,\n })\n }\n }\n }\n\n for (const [cookieId, cookie] of Object.entries(cookies)) {\n const c = cookie as unknown as Record<string, unknown>\n const legalBasis = getCookieLegalBasis(cookieId, categoryIndex)\n\n for (const rule of rules.cookieErrors) {\n if (rule.check(c, legalBasis)) {\n errors.push({\n cookieId,\n field: rule.field,\n message: rule.message(cookieId),\n rule: rule.rule,\n })\n }\n }\n\n for (const rule of rules.cookieWarnings) {\n if (rule.check(c, legalBasis)) {\n warnings.push({\n cookieId,\n field: rule.field,\n message: rule.message(cookieId),\n suggestion: rule.suggestion,\n })\n }\n }\n }\n\n return { valid: errors.length === 0, errors, warnings }\n}\n","import type { MainBanner, GpcBanner, PreferenceModal, ContentValidationError, ContentValidationResult } from '@consenti/types'\nimport { hasVisibleText } from '@consenti/utils'\n\n/**\n * Mandatory-content backstop — the actual enforcement for bulk CSV/JSON import, which bypasses\n * the dashboard wizard's step-by-step gating entirely. Validates already-resolved content (the\n * shape the dashboard now sends per `StoredProfileJson`/`LocaleContentInput` — see\n * `plans/PENDING-profile-storage-buttons-wizard-revamp.md` Phase 3/4), not raw author input.\n *\n * Rules (heading is intentionally optional everywhere — only body text, button labels, and\n * category headings are mandatory):\n * - Main Banner / GPC Banner: `htmlText` mandatory (`hasVisibleText`), every button's `text` mandatory.\n * - Preference Modal: `heading` mandatory, every button's `text` mandatory, every category's `heading` mandatory.\n *\n * Trusts the submitted button/category id set as authoritative (matches this codebase's existing\n * `validateProfileCompliance` convention of validating submitted values, not re-fetching templates\n * to cross-check the id set) — the dashboard always submits one button entry per template button\n * by construction (see `ProfileEditor.tsx` `resolveLocaleContent()`), so a blank `text` on a\n * submitted entry is exactly the case this needs to catch.\n */\nfunction validateBanner(locale: string, section: 'mainBanner' | 'gpcBanner', banner: MainBanner | GpcBanner): ContentValidationError[] {\n const errors: ContentValidationError[] = []\n if (!hasVisibleText(banner.htmlText)) {\n errors.push({ locale, section, field: 'htmlText', message: 'Body text is required' })\n }\n for (const [buttonId, btn] of Object.entries(banner.buttons)) {\n if (!btn.text?.trim()) {\n errors.push({ locale, section, field: `buttons.${buttonId}`, message: `Label for button \"${buttonId}\" is required` })\n }\n }\n return errors\n}\n\nfunction validateModal(locale: string, modal: PreferenceModal): ContentValidationError[] {\n const errors: ContentValidationError[] = []\n if (!modal.heading?.trim()) {\n errors.push({ locale, section: 'preferenceModal', field: 'heading', message: 'Heading is required' })\n }\n for (const [buttonId, btn] of Object.entries(modal.buttons)) {\n if (!btn.text?.trim()) {\n errors.push({ locale, section: 'preferenceModal', field: `buttons.${buttonId}`, message: `Label for button \"${buttonId}\" is required` })\n }\n }\n for (const [categoryId, category] of Object.entries(modal.categories)) {\n if (!category.heading?.trim()) {\n errors.push({ locale, section: 'preferenceModal', field: `categories.${categoryId}.heading`, message: `Heading for category \"${categoryId}\" is required` })\n }\n }\n return errors\n}\n\n/**\n * Validates every locale in `localeContents` (default locale + any non-default locales from\n * `localeContent`) against the mandatory-content matrix. GPC banner rules only apply to a locale\n * that actually submitted `gpcBanner` content — a profile with `gpcMode: 'ignore'` (or a locale\n * nobody's authored a GPC variant for) simply omits it, which isn't itself an error.\n */\nexport function validateProfileContent(\n localeContents: Record<string, { mainBanner: MainBanner; gpcBanner?: GpcBanner; preferenceModal: PreferenceModal }>,\n): ContentValidationResult {\n const errors: ContentValidationError[] = []\n for (const [locale, content] of Object.entries(localeContents)) {\n errors.push(...validateBanner(locale, 'mainBanner', content.mainBanner))\n if (content.gpcBanner) errors.push(...validateBanner(locale, 'gpcBanner', content.gpcBanner))\n errors.push(...validateModal(locale, content.preferenceModal))\n }\n return { valid: errors.length === 0, errors }\n}\n","import type { StorageAdapter, AuthConfig, CookieMap, CategoryMap, MainBanner, GpcBanner, PreferenceModal } from '@consenti/types'\nimport { COMPLIANCE_GROUPS } from '@consenti/utils'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport { validateCreateProfile, validateUpdateProfile, castCreateProfile, castUpdateProfile } from '../../validators/profile.validator'\nimport type { ProfileService } from '../../services/profile.service'\nimport { validateProfileCompliance } from '../../services/compliance-validator.service'\nimport { validateProfileContent } from '../../services/profile-content-validator.service'\n\n/** Builds the `{locale: {mainBanner, gpcBanner?, preferenceModal}}` map `validateProfileContent`\n * needs — default locale's content lives directly on `profileJson`, every other locale in the\n * sibling `localeContent` field (see `StoredProfileJson`/`LocaleContentInput`). */\nfunction collectLocaleContents(\n profileJson: { defaultLocale?: string; mainBanner?: MainBanner; gpcBanner?: GpcBanner; preferenceModal?: PreferenceModal } | undefined,\n localeContent: Record<string, { mainBanner: MainBanner; gpcBanner?: GpcBanner; preferenceModal: PreferenceModal }> | undefined,\n): Record<string, { mainBanner: MainBanner; gpcBanner?: GpcBanner; preferenceModal: PreferenceModal }> {\n const result: Record<string, { mainBanner: MainBanner; gpcBanner?: GpcBanner; preferenceModal: PreferenceModal }> = { ...localeContent }\n if (profileJson?.defaultLocale && profileJson.mainBanner && profileJson.preferenceModal) {\n result[profileJson.defaultLocale] = {\n mainBanner: profileJson.mainBanner,\n ...(profileJson.gpcBanner ? { gpcBanner: profileJson.gpcBanner } : {}),\n preferenceModal: profileJson.preferenceModal,\n }\n }\n return result\n}\n\nexport function buildAdminProfileRoutes(\n service: ProfileService,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n function tenantAllowed(user: { allowedTenants: string[] } | null, tenantId: string): boolean {\n if (!user) return false\n return user.allowedTenants.length === 0 || user.allowedTenants.includes(tenantId)\n }\n\n return {\n 'GET /profiles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'profile:view')\n if (denied) return denied\n if (!tenantAllowed(user, 'default')) return json(200, [])\n const url = new URL(req.url)\n if (url.searchParams.get('summary') === '1') {\n return json(200, await service.listSummary())\n }\n return json(200, await service.list())\n }),\n\n // Profile-id directories on disk with no matching DB row (deleted profiles whose version\n // snapshots weren't). Must be registered before GET /profiles/:id — routes are matched in\n // registration order and :id's pattern would otherwise swallow this literal path first.\n 'GET /profiles/archived': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await service.listArchivedProfiles())\n }),\n\n 'GET /profiles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const profile = await service.get(p['id'] ?? '')\n if (!profile) return errorResponse(404, 'Profile not found')\n return json(200, profile)\n }),\n\n // Standalone compliance validation — used by dashboard wizard at step 2\n 'POST /profiles/validate': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const body = await parseJsonBody(req) as { complianceGroup?: string; cookies?: CookieMap; categories?: CategoryMap }\n if (!body.complianceGroup || !body.cookies || typeof body.cookies !== 'object' || !body.categories || typeof body.categories !== 'object') {\n return errorResponse(400, 'complianceGroup, cookies, and categories are required')\n }\n return json(200, validateProfileCompliance(body.cookies, body.categories, body.complianceGroup))\n }),\n\n // Returns active profile per compliance group for the coverage panel\n 'GET /compliance-coverage': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const groups: Record<string, {\n label: string\n compliances: readonly string[]\n activeProfile: { id: string; name: string } | null\n }> = {}\n for (const [groupId, groupDef] of Object.entries(COMPLIANCE_GROUPS)) {\n const profile = await service.findActiveByComplianceGroup(groupId)\n groups[groupId] = {\n label: groupDef.label,\n compliances: groupDef.compliances,\n activeProfile: profile ? { id: profile.id, name: profile.name } : null,\n }\n }\n return json(200, { groups })\n }),\n\n 'POST /profiles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody(req) as Record<string, unknown>\n const validation = validateCreateProfile(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid body')\n const input = castCreateProfile(body, 'default')\n\n // Mandatory-content validation (defence in depth — the dashboard wizard already gates on\n // this client-side, but bulk CSV/JSON import bypasses that entirely)\n const contentValidation = validateProfileContent(collectLocaleContents(input.profileJson, input.localeContent))\n if (!contentValidation.valid) {\n return errorResponse(422, 'Profile content is missing required fields', { errors: contentValidation.errors })\n }\n\n // Compliance validation (defence in depth — dashboard already validates client-side)\n const complianceGroup = (input.profileJson as { complianceGroup?: string }).complianceGroup\n const customComplianceGroup = (input.profileJson as { customComplianceGroup?: string }).customComplianceGroup\n const choice = typeof body['choice'] === 'string' ? body['choice'] : undefined\n\n if (complianceGroup) {\n const cookies = (input.profileJson as { cookies?: CookieMap }).cookies ?? {}\n const categories = (input.profileJson as { preferenceModal?: { categories?: CategoryMap } }).preferenceModal?.categories ?? {}\n const complianceValidation = validateProfileCompliance(cookies, categories, complianceGroup)\n if (!complianceValidation.valid) {\n return errorResponse(422, 'Profile does not meet compliance requirements', {\n errors: complianceValidation.errors,\n warnings: complianceValidation.warnings,\n })\n }\n\n // Conflict detection: only one active profile per compliance group\n const existing = await service.findActiveByComplianceGroup(complianceGroup)\n if (existing) {\n if (!choice) {\n return json(200, { conflict: { id: existing.id, name: existing.name }, requiresChoice: true })\n }\n if (choice === 'deactivate') {\n await service.deactivate(existing.id)\n }\n // choice === 'inactive' → fall through; new profile saves without isActive\n }\n\n const profile = await service.create(input)\n return json(201, { profile, warnings: complianceValidation.warnings })\n }\n\n // No built-in compliance group — a custom group has no COMPLIANCE_GROUPS rules to\n // validate against, but \"only one active profile per group\" still applies so the\n // widget's compliance.type lookup for it resolves unambiguously.\n if (customComplianceGroup) {\n const existing = await service.findActiveByComplianceGroup(customComplianceGroup)\n if (existing) {\n if (!choice) {\n return json(200, { conflict: { id: existing.id, name: existing.name }, requiresChoice: true })\n }\n if (choice === 'deactivate') {\n await service.deactivate(existing.id)\n }\n }\n }\n\n const profile = await service.create(input)\n return json(201, profile)\n }),\n\n 'PUT /profiles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n const body = await parseJsonBody(req) as Record<string, unknown>\n const validation = validateUpdateProfile(body)\n if (!validation.valid) return errorResponse(400, validation.error ?? 'Invalid body')\n const input = castUpdateProfile(body)\n\n // Mandatory-content validation — only when this save actually touches content; a\n // partial update (e.g. toggling darkMode) with no profileJson/localeContent has nothing\n // to validate.\n const localeContents = collectLocaleContents(input.profileJson, input.localeContent)\n if (Object.keys(localeContents).length > 0) {\n const contentValidation = validateProfileContent(localeContents)\n if (!contentValidation.valid) {\n return errorResponse(422, 'Profile content is missing required fields', { errors: contentValidation.errors })\n }\n }\n\n // Compliance validation\n const complianceGroup = (input.profileJson as { complianceGroup?: string } | undefined)?.complianceGroup\n const customComplianceGroup = (input.profileJson as { customComplianceGroup?: string } | undefined)?.customComplianceGroup\n const choice = typeof body['choice'] === 'string' ? body['choice'] : undefined\n\n if (complianceGroup) {\n const cookies = (input.profileJson as { cookies?: CookieMap } | undefined)?.cookies ?? {}\n const categories = (input.profileJson as { preferenceModal?: { categories?: CategoryMap } } | undefined)?.preferenceModal?.categories ?? {}\n const complianceValidation = validateProfileCompliance(cookies, categories, complianceGroup)\n if (!complianceValidation.valid) {\n return errorResponse(422, 'Profile does not meet compliance requirements', {\n errors: complianceValidation.errors,\n warnings: complianceValidation.warnings,\n })\n }\n\n // Conflict detection: another active profile for same complianceGroup\n const existing = await service.findActiveByComplianceGroup(complianceGroup)\n if (existing && existing.id !== id) {\n if (!choice) {\n return json(200, { conflict: { id: existing.id, name: existing.name }, requiresChoice: true })\n }\n if (choice === 'deactivate') {\n await service.deactivate(existing.id)\n }\n }\n\n const profile = await service.update(id, input)\n return json(200, { profile, warnings: complianceValidation.warnings })\n }\n\n // Custom group: no COMPLIANCE_GROUPS rules apply, but conflict detection still does.\n if (customComplianceGroup) {\n const existing = await service.findActiveByComplianceGroup(customComplianceGroup)\n if (existing && existing.id !== id) {\n if (!choice) {\n return json(200, { conflict: { id: existing.id, name: existing.name }, requiresChoice: true })\n }\n if (choice === 'deactivate') {\n await service.deactivate(existing.id)\n }\n }\n }\n\n const profile = await service.update(id, input)\n return json(200, profile)\n }),\n\n 'DELETE /profiles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:delete')\n if (denied) return denied\n await service.delete(p['id'] ?? '')\n return json(200, { success: true })\n }),\n\n 'POST /profiles/:id/copy': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const id = p['id'] ?? ''\n const existing = await service.get(id)\n if (!existing) return errorResponse(404, 'Profile not found')\n const body = await parseJsonBody(req) as Record<string, unknown> | null\n const name = typeof body?.['name'] === 'string' ? body['name'] : undefined\n const copy = await service.copy(id, name)\n return json(201, copy)\n }),\n\n 'POST /profiles/:id/activate': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n const body = await parseJsonBody(req) as Record<string, unknown> | null\n const choice = typeof body?.['choice'] === 'string' ? body['choice'] : undefined\n\n const profile = await service.get(id)\n if (!profile) return errorResponse(404, 'Profile not found')\n\n const pj = profile.profileJson as { complianceGroup?: string; customComplianceGroup?: string }\n const groupKey = pj.complianceGroup || pj.customComplianceGroup\n if (groupKey) {\n const existing = await service.findActiveByComplianceGroup(groupKey)\n if (existing && existing.id !== id) {\n if (!choice) {\n return json(200, { conflict: { id: existing.id, name: existing.name }, requiresChoice: true })\n }\n if (choice === 'deactivate') {\n await service.deactivate(existing.id)\n }\n }\n }\n\n const activated = await service.activate(id)\n return json(200, activated)\n }),\n\n 'POST /profiles/:id/deactivate': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const profile = await service.deactivate(p['id'] ?? '')\n return json(200, profile)\n }),\n\n 'GET /profiles/:id/versions': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await service.listVersions(p['id'] ?? ''))\n }),\n\n 'GET /profiles/:id/versions/:entryId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const url = new URL(req.url)\n const locale = url.searchParams.get('locale') ?? 'default'\n const entryId = p['entryId'] ?? ''\n if (!entryId) return errorResponse(400, 'Invalid version id')\n const content = await service.getVersionFile(p['id'] ?? '', entryId, locale)\n if (!content) return errorResponse(404, 'Version file not found')\n return new Response(content, { headers: { 'Content-Type': 'application/json' } })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, ConsentFilters } from '@consenti/types'\nimport { json, getQueryParam, getPagination } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminConsentRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /consents': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'consent:view')\n if (denied) return denied\n if (user?.allowedTenants.length && !user.allowedTenants.includes('default')) return json(200, { items: [], total: 0, page: 1, limit: 50 })\n const url = new URL(req.url)\n const profileId = getQueryParam(url, 'profileId')\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const q = getQueryParam(url, 'q')\n const pagination = getPagination(url)\n if ('error' in pagination) return errorResponse(400, pagination.error)\n const { page, limit } = pagination\n const filters: ConsentFilters = {\n tenantId: 'default',\n page,\n limit,\n ...(profileId !== undefined ? { profileId } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n ...(q !== undefined ? { q } : {}),\n }\n const records = await storage.getConsents(filters)\n return json(200, records)\n }),\n\n 'GET /consents/:visitorId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'consent:view')\n if (denied) return denied\n if (user?.allowedTenants.length && !user.allowedTenants.includes('default')) return errorResponse(403, 'Forbidden')\n const record = await storage.getConsent(p['visitorId'] ?? '')\n if (!record) return errorResponse(404, 'Consent not found')\n return json(200, record)\n }),\n\n 'GET /consents/:visitorId/history': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'consent:view')\n if (denied) return denied\n if (user?.allowedTenants.length && !user.allowedTenants.includes('default')) return errorResponse(403, 'Forbidden')\n const history = await storage.getConsentHistory(p['visitorId'] ?? '')\n return json(200, history)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, VisitorFilters } from '@consenti/types'\nimport { json, getQueryParam, getPagination } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\n/** See NOTICE_SHOWN_ENABLED in routes/public/notice.routes.ts — same dormant feature, kept for future reference. */\nconst NOTICE_SHOWN_ENABLED: boolean = false\n\nexport function buildAdminVisitorRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /visitors': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'visitor:view')\n if (denied) return denied\n if (user?.allowedTenants.length && !user.allowedTenants.includes('default')) return json(200, { items: [], total: 0, page: 1, limit: 50 })\n const url = new URL(req.url)\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const q = getQueryParam(url, 'q')\n const pagination = getPagination(url)\n if ('error' in pagination) return errorResponse(400, pagination.error)\n const { page, limit } = pagination\n const filters: VisitorFilters = {\n tenantId: 'default',\n page,\n limit,\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n ...(q !== undefined ? { q } : {}),\n }\n const visitors = await storage.getVisitors(filters)\n return json(200, visitors)\n }),\n\n 'GET /visitors/:visitorId/notice-shown': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n if (!NOTICE_SHOWN_ENABLED) return errorResponse(404, 'Not found')\n\n const { denied } = await auth(req, 'visitor:view')\n if (denied) return denied\n const records = await storage.getNoticeShownForVisitor(p['visitorId'] ?? '')\n return json(200, records)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport type { UserService } from '../../services/user.service'\n\nexport function buildAdminUserRoutes(\n userService: UserService,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /users': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'user:view')\n if (denied) return denied\n const users = await userService.list()\n return json(200, users)\n }),\n\n 'GET /users/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'user:view')\n if (denied) return denied\n const user = await userService.get(p['id'] ?? '')\n if (!user) return errorResponse(404, 'User not found')\n const roles = await userService.getRoles(user.id)\n return json(200, { ...user, roles: roles.map(r => ({ id: r.id, name: r.name })) })\n }),\n\n 'POST /users': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'user:create')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || !b['name']) return errorResponse(400, 'name is required')\n if (typeof b['email'] !== 'string' || !b['email']) return errorResponse(400, 'email is required')\n if (typeof b['password'] !== 'string' || !b['password']) return errorResponse(400, 'password is required')\n if (b['password'].length < 12) return errorResponse(400, 'password must be at least 12 characters')\n const created = await userService.create(\n {\n name: b['name'],\n email: b['email'],\n password: b['password'],\n ...(typeof b['roleId'] === 'string' ? { roleId: b['roleId'] } : {}),\n ...(Array.isArray(b['allowedTenants']) ? { allowedTenants: b['allowedTenants'] as string[] } : {}),\n },\n actor!.sub,\n )\n return json(201, created)\n }),\n\n 'PUT /users/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'user:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['password'] === 'string' && b['password'].length < 12) {\n return errorResponse(400, 'password must be at least 12 characters')\n }\n const updated = await userService.update(\n id,\n {\n ...(typeof b['name'] === 'string' ? { name: b['name'] } : {}),\n ...(typeof b['email'] === 'string' ? { email: b['email'] } : {}),\n ...(b['isActive'] != null ? { isActive: Boolean(b['isActive']) } : {}),\n ...(typeof b['password'] === 'string' ? { password: b['password'] } : {}),\n ...(Array.isArray(b['allowedTenants']) ? { allowedTenants: b['allowedTenants'] as string[] } : {}),\n },\n actor!.sub,\n )\n return json(200, updated)\n }),\n\n 'DELETE /users/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'user:delete')\n if (denied) return denied\n const id = p['id'] ?? ''\n if (id === actor!.sub) return errorResponse(400, 'Cannot delete your own account')\n await userService.delete(id, actor!.sub)\n return json(200, { success: true })\n }),\n\n 'POST /users/:id/roles': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['roleId'] !== 'string') return errorResponse(400, 'roleId is required')\n const tenantId = typeof b['tenantId'] === 'string' ? b['tenantId'] : undefined\n await userService.assignRole(p['id'] ?? '', b['roleId'], tenantId)\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'user.role_assigned',\n resourceType: 'user',\n ...(p['id'] != null ? { resourceId: p['id'] } : {}),\n newData: { roleId: b['roleId'] },\n })\n return json(200, { success: true })\n }),\n\n 'DELETE /users/:id/roles/:roleId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:update')\n if (denied) return denied\n const url = new URL(req.url)\n const revokedTenantId = url.searchParams.get('tenantId') ?? undefined\n await userService.revokeRole(p['id'] ?? '', p['roleId'] ?? '', revokedTenantId)\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'user.role_revoked',\n resourceType: 'user',\n ...(p['id'] != null ? { resourceId: p['id'] } : {}),\n oldData: { roleId: p['roleId'] },\n })\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminRoleRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /roles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:view')\n if (denied) return denied\n const roles = await storage.getRoles('default')\n return json(200, roles)\n }),\n\n 'GET /roles/:id/permissions': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:view')\n if (denied) return denied\n const perms = await storage.getPermissionsForRole(p['id'] ?? '')\n return json(200, perms)\n }),\n\n 'POST /roles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:create')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || !b['name']) return errorResponse(400, 'name is required')\n const role = await storage.createRole({\n tenantId: 'default',\n name: b['name'],\n ...(typeof b['description'] === 'string' ? { description: b['description'] } : {}),\n })\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'role.created',\n resourceType: 'role',\n resourceId: role.id,\n newData: role,\n })\n return json(201, role)\n }),\n\n 'PUT /roles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n const role = await storage.updateRole(p['id'] ?? '', {\n ...(typeof b['name'] === 'string' ? { name: b['name'] } : {}),\n ...(typeof b['description'] === 'string' ? { description: b['description'] } : {}),\n })\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'role.updated',\n resourceType: 'role',\n resourceId: role.id,\n newData: role,\n })\n return json(200, role)\n }),\n\n 'DELETE /roles/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user: actor, denied } = await auth(req, 'role:delete')\n if (denied) return denied\n await storage.deleteRole(p['id'] ?? '')\n await storage.createLog({\n tenantId: 'default',\n userId: actor!.sub,\n action: 'role.deleted',\n resourceType: 'role',\n ...(p['id'] != null ? { resourceId: p['id'] } : {}),\n })\n return json(200, { success: true })\n }),\n\n 'GET /permissions': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:view')\n if (denied) return denied\n const perms = await storage.getAllPermissions()\n return json(200, perms)\n }),\n\n 'POST /roles/:id/permissions': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['permissionId'] !== 'string') return errorResponse(400, 'permissionId is required')\n await storage.assignPermissionToRole(p['id'] ?? '', b['permissionId'])\n return json(200, { success: true })\n }),\n\n 'DELETE /roles/:id/permissions/:permissionId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'role:update')\n if (denied) return denied\n await storage.revokePermissionFromRole(p['id'] ?? '', p['permissionId'] ?? '')\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, AuditFilters } from '@consenti/types'\nimport { json, getQueryParam, getPagination } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminAuditRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /audit': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'audit:view')\n if (denied) return denied\n if (user?.allowedTenants.length && !user.allowedTenants.includes('default')) return json(200, { items: [], total: 0, page: 1, limit: 50 })\n const url = new URL(req.url)\n const action = getQueryParam(url, 'action')\n const resourceType = getQueryParam(url, 'resourceType')\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const q = getQueryParam(url, 'q')\n const pagination = getPagination(url)\n if ('error' in pagination) return errorResponse(400, pagination.error)\n const { page, limit } = pagination\n const filters: AuditFilters = {\n tenantId: 'default',\n page,\n limit,\n ...(action !== undefined ? { action } : {}),\n ...(resourceType !== undefined ? { resourceType } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n ...(q !== undefined ? { q } : {}),\n }\n const logs = await storage.getLogs(filters)\n return json(200, logs)\n }),\n\n 'GET /audit/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req, 'audit:view')\n if (denied) return denied\n if (user?.allowedTenants.length && !user.allowedTenants.includes('default')) return errorResponse(403, 'Forbidden')\n const log = await storage.getAuditLogById(p['id'] ?? '')\n if (!log || log.tenantId !== 'default') return errorResponse(404, 'Audit log entry not found')\n return json(200, log)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, getQueryInt } from '../../utils/http'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport type { StatsService } from '../../services/stats.service'\n\nexport function buildAdminStatsRoutes(\n storage: StorageAdapter,\n stats: StatsService,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'consent:view') }\n }\n\n return {\n 'GET /stats/overview': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const overview = await stats.getOverview('default')\n return json(200, overview)\n }),\n\n 'GET /stats/categories': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const categories = await stats.getCategories('default')\n return json(200, categories)\n }),\n\n 'GET /stats/timeline': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const url = new URL(req.url)\n const days = getQueryInt(url, 'days', 30)\n const timeline = await stats.getTimeline('default', days)\n return json(200, timeline)\n }),\n\n 'GET /stats/countries': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const countries = await stats.getCountries('default')\n return json(200, countries)\n }),\n\n 'GET /stats/gpc': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const gpc = await stats.getGpc('default')\n return json(200, gpc)\n }),\n\n 'GET /stats/aggregate': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const tenants = await stats.getTenants()\n const rows = await Promise.all(\n tenants.map(async t => {\n const s = await stats.getOverview(t.id)\n return { tenantId: t.id, tenantName: t.name, ...s }\n }),\n )\n const aggregate = rows.reduce(\n (acc, r) => ({\n totalConsents: acc.totalConsents + r.totalConsents,\n totalVisitors: acc.totalVisitors + r.totalVisitors,\n gpcDetectedCount: acc.gpcDetectedCount + r.gpcDetectedCount,\n }),\n { totalConsents: 0, totalVisitors: 0, gpcDetectedCount: 0 },\n )\n return json(200, { aggregate, perTenant: rows })\n }),\n }\n}\n","import type {\n StorageAdapter, AuthConfig, ConsentFilters, AuditFilters,\n ConsentDbRecord, AuditLog,\n} from '@consenti/types'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { errorResponse } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport { getQueryParam } from '../../utils/http'\nimport type { ProfileService } from '../../services/profile.service'\n\nfunction escapeCsv(val: string | number | boolean | null | undefined): string {\n const s = val == null ? '' : String(val)\n if (s.includes(',') || s.includes('\"') || s.includes('\\n')) {\n return `\"${s.replace(/\"/g, '\"\"')}\"`\n }\n return s\n}\n\nfunction consentToCsvRow(r: ConsentDbRecord): string {\n return [\n r.id, r.visitorId, r.profileId, r.locale,\n JSON.stringify(r.consentJson), r.gpcDetected ? '1' : '0',\n r.source, r.signature ?? '', r.createdAt, r.updatedAt,\n ].map(escapeCsv).join(',') + '\\n'\n}\n\nfunction auditToCsvRow(r: AuditLog): string {\n return [\n r.id, r.userId ?? '', r.action, r.resourceType, r.resourceId ?? '', r.createdAt,\n ].map(escapeCsv).join(',') + '\\n'\n}\n\nexport function buildAdminExportRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n profileService: ProfileService,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'export:run') }\n }\n\n return {\n 'GET /export/consents': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const url = new URL(req.url)\n const format = getQueryParam(url, 'format') ?? 'csv'\n const profileId = getQueryParam(url, 'profileId')\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const filters: ConsentFilters = {\n tenantId: 'default',\n ...(profileId !== undefined ? { profileId } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n\n if (format === 'json') {\n const records: ConsentDbRecord[] = []\n for await (const r of storage.streamConsents(filters)) records.push(r)\n return new Response(JSON.stringify(records), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Disposition': 'attachment; filename=\"consents.json\"',\n },\n })\n }\n\n const enc = new TextEncoder()\n const header = 'id,visitor_id,profile_id,locale,consent_json,gpc_detected,source,signature,created_at,updated_at\\n'\n const stream = new ReadableStream({\n async start(controller) {\n controller.enqueue(enc.encode(header))\n for await (const r of storage.streamConsents(filters)) {\n controller.enqueue(enc.encode(consentToCsvRow(r)))\n }\n controller.close()\n },\n })\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': 'attachment; filename=\"consents.csv\"',\n },\n })\n }),\n\n 'GET /export/consents/xlsx': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n\n interface XlsxModule {\n utils: {\n book_new(): object\n json_to_sheet(rows: object[]): object\n book_append_sheet(wb: object, ws: object, name: string): void\n }\n write(wb: object, opts: { type: string; bookType: string }): Uint8Array\n }\n let XlsxMod: XlsxModule | null = null\n try {\n // @ts-expect-error -- xlsx is an optional peer dependency; install it to use XLSX export\n XlsxMod = (await import('xlsx')) as XlsxModule\n } catch {\n return new Response(JSON.stringify({ error: 'xlsx peer dependency not installed. Run: npm install xlsx' }), {\n status: 501,\n headers: { 'Content-Type': 'application/json' },\n })\n }\n if (!XlsxMod) return new Response(null, { status: 501 })\n\n const url = new URL(req.url)\n const profileId = url.searchParams.get('profileId') ?? undefined\n const from = url.searchParams.get('from') ?? undefined\n const to = url.searchParams.get('to') ?? undefined\n const filters: ConsentFilters = {\n tenantId: 'default',\n ...(profileId !== undefined ? { profileId } : {}),\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n\n const rows: Record<string, unknown>[] = []\n for await (const r of storage.streamConsents(filters)) {\n rows.push({\n id: r.id,\n visitor_id: r.visitorId,\n profile_id: r.profileId,\n locale: r.locale,\n consent_json: JSON.stringify(r.consentJson),\n gpc_detected: r.gpcDetected ? 1 : 0,\n age_verified: r.ageVerified ? 1 : 0,\n tcf_string: r.tcfString ?? '',\n signature: r.signature ?? '',\n source: r.source,\n created_at: r.createdAt,\n updated_at: r.updatedAt,\n })\n }\n\n const wb = XlsxMod.utils.book_new()\n const ws = XlsxMod.utils.json_to_sheet(rows)\n XlsxMod.utils.book_append_sheet(wb, ws, 'Consents')\n const buf = XlsxMod.write(wb, { type: 'buffer', bookType: 'xlsx' })\n return new Response(buf as unknown as BodyInit, {\n status: 200,\n headers: {\n 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'Content-Disposition': 'attachment; filename=\"consents.xlsx\"',\n },\n })\n }),\n\n 'GET /export/translations/:profileId': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const profileId = p['profileId'] ?? ''\n const profile = await storage.getProfile(profileId)\n if (!profile) return errorResponse(404, 'Profile not found')\n\n const pj = profile.profileJson\n const locales = pj.locales?.length ? pj.locales : [profile.defaultLocale]\n // Default locale's content lives directly on the row; every other locale lives only in\n // its own on-disk version file (see ProfileService) — read each independently, same as\n // getResolved() does for a single locale.\n const translations: Record<string, Record<string, unknown>> = {}\n for (const locale of locales) {\n if (locale === profile.defaultLocale) {\n translations[locale] = { mainBanner: pj.mainBanner, ...(pj.gpcBanner ? { gpcBanner: pj.gpcBanner } : {}), preferenceModal: pj.preferenceModal }\n continue\n }\n const raw = await profileService.getVersionFile(profileId, String(profile.version), locale)\n if (!raw) continue\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>\n translations[locale] = { mainBanner: parsed['mainBanner'], ...(parsed['gpcBanner'] ? { gpcBanner: parsed['gpcBanner'] } : {}), preferenceModal: parsed['preferenceModal'] }\n } catch { /* skip an unreadable/corrupt locale file */ }\n }\n if (Object.keys(translations).length === 0) {\n return new Response('locale,field,value\\n', {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': `attachment; filename=\"${profileId}-translations.csv\"`,\n },\n })\n }\n\n const rows: string[] = ['locale,section,field,value']\n for (const [locale, t] of Object.entries(translations)) {\n for (const [section, content] of Object.entries(t)) {\n if (typeof content === 'object' && content !== null) {\n for (const [field, value] of Object.entries(content as Record<string, unknown>)) {\n if (typeof value === 'string') {\n rows.push([locale, section, field, value].map(escapeCsv).join(','))\n }\n }\n }\n }\n }\n\n return new Response(rows.join('\\n') + '\\n', {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': `attachment; filename=\"${profileId}-translations.csv\"`,\n },\n })\n }),\n\n 'GET /export/audit': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const url = new URL(req.url)\n const format = getQueryParam(url, 'format') ?? 'csv'\n const from = getQueryParam(url, 'from')\n const to = getQueryParam(url, 'to')\n const filters: AuditFilters = {\n tenantId: 'default',\n ...(from !== undefined ? { from } : {}),\n ...(to !== undefined ? { to } : {}),\n }\n\n if (format === 'json') {\n const logs: AuditLog[] = []\n for await (const r of storage.streamAuditLogs(filters)) logs.push(r)\n return new Response(JSON.stringify(logs), {\n status: 200,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Disposition': 'attachment; filename=\"audit.json\"',\n },\n })\n }\n\n const enc = new TextEncoder()\n const header = 'id,user_id,action,resource_type,resource_id,created_at\\n'\n const stream = new ReadableStream({\n async start(controller) {\n controller.enqueue(enc.encode(header))\n for await (const r of storage.streamAuditLogs(filters)) {\n controller.enqueue(enc.encode(auditToCsvRow(r)))\n }\n controller.close()\n },\n })\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/csv; charset=utf-8',\n 'Content-Disposition': 'attachment; filename=\"audit.csv\"',\n },\n })\n }),\n }\n}\n","import { randomBytes, createHash } from 'node:crypto'\nimport type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json } from '../../utils/http'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nfunction generateApiKey(): { raw: string; hash: string } {\n const raw = `ck_live_${randomBytes(12).toString('hex')}${randomBytes(12).toString('hex')}}`\n const hash = createHash('sha256').update(raw).digest('hex')\n return { raw, hash }\n}\n\nexport function buildAdminApiKeyRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, 'settings:update') }\n }\n\n return {\n 'GET /apikeys': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const keys = await storage.getApiKeys('default')\n const now = new Date()\n const result = await Promise.all(keys.map(async k => {\n // Lazy expiration: no background sweep — a key someone actually views here self-heals\n // to accurate status, same idea as the real auth path in tenant.middleware.ts.\n const expired = k.isActive && !!k.expireBy && new Date(k.expireBy) <= now\n if (expired) await storage.revokeApiKey(k.id).catch(() => { })\n return {\n id: k.id, name: k.name, isActive: expired ? false : k.isActive,\n createdAt: k.createdAt, updatedAt: k.updatedAt,\n ...(k.createdBy !== undefined ? { createdBy: k.createdBy } : {}),\n ...(k.expireBy !== undefined ? { expireBy: k.expireBy } : {}),\n }\n }))\n return json(200, result)\n }),\n\n 'POST /apikeys': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { user, denied } = await auth(req)\n if (denied) return denied\n const body = await req.json() as { name?: string; tenantId?: string; expireBy?: string }\n if (!body.name) return json(400, { error: 'name is required' })\n if (body.expireBy !== undefined && Number.isNaN(new Date(body.expireBy).getTime())) {\n return json(400, { error: 'expireBy must be a valid date' })\n }\n const { raw, hash } = generateApiKey()\n const created = await storage.createApiKey({\n tenantId: body.tenantId ?? 'default',\n name: body.name,\n keyHash: hash,\n ...(user ? { createdBy: user.sub } : {}),\n ...(body.expireBy !== undefined ? { expireBy: body.expireBy } : {}),\n })\n return json(201, { id: created.id, name: created.name, key: raw, createdAt: created.createdAt })\n }),\n\n 'DELETE /apikeys/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n await storage.revokeApiKey(p['id'] ?? '')\n // 204 forbids a body — the JSON.stringify(null) `json()` would normally send trips\n // the Response constructor's null-body-status check (\"Invalid response status code 204\").\n return new Response(null, { status: 204 })\n }),\n\n // Re-enables a previously revoked key — same hash, no new secret to distribute. Distinct\n // from creating a new key: whatever system already has the old raw secret saved starts\n // working again immediately.\n 'POST /apikeys/:id/reactivate': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n await storage.reactivateApiKey(p['id'] ?? '')\n return new Response(null, { status: 204 })\n }),\n\n // Permanently removes the row — unlike DELETE /apikeys/:id (revoke), this cannot be undone.\n 'DELETE /apikeys/:id/permanent': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n await storage.deleteApiKey(p['id'] ?? '')\n return new Response(null, { status: 204 })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminTenantRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request, perm: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /tenants': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const tenants = await storage.getTenants()\n return json(200, tenants)\n }),\n\n 'POST /tenants': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (typeof b['name'] !== 'string' || typeof b['slug'] !== 'string') {\n return errorResponse(400, 'name and slug are required')\n }\n const tenant = await storage.createTenant({ name: b['name'], slug: b['slug'] })\n return json(201, tenant)\n }),\n\n 'PUT /tenants/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n const tenant = await storage.updateTenant(p['id'] ?? '', {\n ...(typeof b['name'] === 'string' ? { name: b['name'] } : {}),\n ...(typeof b['slug'] === 'string' ? { slug: b['slug'] } : {}),\n })\n return json(200, tenant)\n }),\n\n 'DELETE /tenants/:id': async (req: Request, p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'settings:update')\n if (denied) return denied\n const id = p['id'] ?? ''\n if (id === 'default') return errorResponse(400, 'Cannot delete the default tenant')\n await storage.deleteTenant(id)\n return json(200, { success: true })\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\n/** Tenant-wide dashboard settings — the public and admin API origin allowlists shown on the\n * API Config page. Single-tenant deployments always use `'default'`. */\nexport function buildAdminSettingsRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, 'settings:update') }\n }\n\n return {\n 'GET /settings': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const settings = await storage.getSettings('default')\n return json(200, settings)\n }),\n\n 'PATCH /settings': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const body = await parseJsonBody(req)\n if (!body || typeof body !== 'object') return errorResponse(400, 'Invalid body')\n const b = body as Record<string, unknown>\n if (b['allowedOrigins'] !== undefined && !Array.isArray(b['allowedOrigins'])) {\n return errorResponse(400, 'allowedOrigins must be an array of strings')\n }\n if (b['adminAllowedOrigins'] !== undefined && !Array.isArray(b['adminAllowedOrigins'])) {\n return errorResponse(400, 'adminAllowedOrigins must be an array of strings')\n }\n const settings = await storage.updateSettings('default', {\n ...(b['allowedOrigins'] !== undefined ? { allowedOrigins: b['allowedOrigins'] as string[] } : {}),\n ...(b['adminAllowedOrigins'] !== undefined ? { adminAllowedOrigins: b['adminAllowedOrigins'] as string[] } : {}),\n })\n return json(200, settings)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { getGvl } from '../../tcf/gvl-cache'\nimport { json } from '../../utils/http'\nimport { withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminTcfRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'consent:view') }\n }\n\n return {\n 'GET /tcf/vendors': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const gvl = await getGvl()\n if (!gvl) return json(503, { error: 'GVL not available. Enable tcf.enabled in config.' })\n const url = new URL(req.url)\n const search = url.searchParams.get('q')?.toLowerCase()\n const page = Math.max(1, parseInt(url.searchParams.get('page') ?? '1', 10))\n const limit = Math.min(200, Math.max(1, parseInt(url.searchParams.get('limit') ?? '100', 10)))\n const vendors = Object.values(gvl.vendors)\n const filtered = search\n ? vendors.filter(v => v.name.toLowerCase().includes(search))\n : vendors\n const total = filtered.length\n const totalPages = Math.max(1, Math.ceil(total / limit))\n const safePage = Math.min(page, totalPages)\n const start = (safePage - 1) * limit\n return json(200, {\n vendorListVersion: gvl.vendorListVersion,\n total,\n page: safePage,\n totalPages,\n vendors: filtered.slice(start, start + limit),\n })\n }),\n\n 'GET /tcf/purposes': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const gvl = await getGvl()\n if (!gvl) return json(503, { error: 'GVL not available. Enable tcf.enabled in config.' })\n return json(200, { purposes: Object.values(gvl.purposes) })\n }),\n }\n}\n","import { COOKIE_PURPOSE_IDS } from '@consenti/utils'\n\nexport interface TemplateValidationResult {\n valid: boolean\n error?: string\n warnings?: string[]\n}\n\nconst LEGAL_BASES = ['mandatory', 'consent', 'legitimate_interest']\nconst CPRA_CATEGORIES = ['sale', 'sharing', 'sensitive']\n\nexport function validateTemplateCookies(cookies: unknown): TemplateValidationResult {\n if (!cookies || typeof cookies !== 'object' || Array.isArray(cookies)) {\n return { valid: false, error: 'cookies must be an object keyed by parameter id' }\n }\n for (const [id, cookie] of Object.entries(cookies as Record<string, unknown>)) {\n if (!id.trim()) return { valid: false, error: 'cookie id must be non-empty' }\n if (!cookie || typeof cookie !== 'object') return { valid: false, error: `cookies[\"${id}\"] must be an object` }\n const c = cookie as Record<string, unknown>\n if (!COOKIE_PURPOSE_IDS.includes(c['purpose'] as never)) {\n return { valid: false, error: `cookies[\"${id}\"].purpose must be one of: ${COOKIE_PURPOSE_IDS.join(', ')}` }\n }\n if (typeof c['listenGpc'] !== 'boolean') {\n return { valid: false, error: `cookies[\"${id}\"].listenGpc must be a boolean` }\n }\n if (c['preGrant'] !== undefined && typeof c['preGrant'] !== 'boolean') {\n return { valid: false, error: `cookies[\"${id}\"].preGrant must be a boolean` }\n }\n if (c['cpraCategory'] !== undefined && !CPRA_CATEGORIES.includes(c['cpraCategory'] as string)) {\n return { valid: false, error: `cookies[\"${id}\"].cpraCategory must be one of: ${CPRA_CATEGORIES.join(', ')}` }\n }\n }\n return { valid: true }\n}\n\nexport function validateTemplateCategories(categories: unknown): TemplateValidationResult {\n if (!categories || typeof categories !== 'object' || Array.isArray(categories)) {\n return { valid: false, error: 'categories must be an object keyed by category id' }\n }\n const entries = Object.entries(categories as Record<string, unknown>)\n if (entries.length === 0) {\n return { valid: false, error: 'at least one category is required' }\n }\n for (const [id, category] of entries) {\n if (!category || typeof category !== 'object') return { valid: false, error: `categories[\"${id}\"] must be an object` }\n const cat = category as Record<string, unknown>\n if (typeof cat['heading'] !== 'string' || !cat['heading'].trim()) {\n return { valid: false, error: `categories[\"${id}\"].heading is required` }\n }\n if (typeof cat['htmlText'] !== 'string') {\n return { valid: false, error: `categories[\"${id}\"].htmlText is required` }\n }\n if (!LEGAL_BASES.includes(cat['legalBasis'] as string)) {\n return { valid: false, error: `categories[\"${id}\"].legalBasis must be one of: ${LEGAL_BASES.join(', ')}` }\n }\n if (!Array.isArray(cat['cookies'])) {\n return { valid: false, error: `categories[\"${id}\"].cookies must be an array of parameter ids` }\n }\n }\n return { valid: true }\n}\n\n/** Enforces that every parameter id belongs to exactly one category — required for legal-basis derivation to be well-defined. */\nexport function validateExactlyOneCategoryPerCookie(\n cookies: Record<string, unknown>,\n categories: Record<string, { cookies: string[] }>,\n): TemplateValidationResult {\n const membershipCount = new Map<string, number>()\n for (const cookieId of Object.keys(cookies)) membershipCount.set(cookieId, 0)\n for (const category of Object.values(categories)) {\n for (const cookieId of category.cookies) {\n membershipCount.set(cookieId, (membershipCount.get(cookieId) ?? 0) + 1)\n }\n }\n for (const [cookieId, count] of membershipCount) {\n if (count === 0) return { valid: false, error: `Parameter \"${cookieId}\" is not assigned to any category` }\n if (count > 1) return { valid: false, error: `Parameter \"${cookieId}\" is assigned to more than one category` }\n }\n return { valid: true }\n}\n\n/**\n * Purpose ↔ legal basis sanity check. A parameter with a non-`necessary` purpose sitting\n * in a `mandatory` category is a hard error (grants a tracking parameter with no consent\n * gate). A `necessary`-purpose parameter sitting outside a `mandatory` category is only a\n * warning — safer-but-broken direction (risks breaking site functionality), not itself illegal.\n */\nexport function validatePurposeLegalBasisAlignment(\n cookies: Record<string, { purpose?: string }>,\n categories: Record<string, { legalBasis: string; cookies: string[] }>,\n): TemplateValidationResult {\n const categoryIdByCookie = new Map<string, string>()\n for (const [categoryId, category] of Object.entries(categories)) {\n for (const cookieId of category.cookies) categoryIdByCookie.set(cookieId, categoryId)\n }\n\n const warnings: string[] = []\n for (const [cookieId, cookie] of Object.entries(cookies)) {\n const categoryId = categoryIdByCookie.get(cookieId)\n if (!categoryId) continue\n const legalBasis = categories[categoryId]!.legalBasis\n const purpose = cookie.purpose\n\n if (legalBasis === 'mandatory' && purpose !== 'necessary') {\n return {\n valid: false,\n error: `Parameter \"${cookieId}\" has purpose \"${purpose ?? 'unknown'}\" but is assigned to a Strictly Necessary category — this grants a tracking parameter with no consent gate.`,\n }\n }\n if (legalBasis !== 'mandatory' && purpose === 'necessary') {\n warnings.push(`Parameter \"${cookieId}\" has purpose \"necessary\" but is assigned to a non-mandatory category — this may break site functionality since it can be denied.`)\n }\n }\n return { valid: true, ...(warnings.length > 0 ? { warnings } : {}) }\n}\n\n/** Full save-time validation for a Consent Template: shape checks, exactly-one-category membership, purpose/legal-basis alignment. */\nexport function validateConsentTemplate(cookies: unknown, categories: unknown): TemplateValidationResult {\n const cookiesResult = validateTemplateCookies(cookies)\n if (!cookiesResult.valid) return cookiesResult\n const categoriesResult = validateTemplateCategories(categories)\n if (!categoriesResult.valid) return categoriesResult\n\n const cookiesMap = cookies as Record<string, { purpose?: string }>\n const categoriesMap = categories as Record<string, { legalBasis: string; cookies: string[] }>\n\n const membershipResult = validateExactlyOneCategoryPerCookie(cookiesMap, categoriesMap)\n if (!membershipResult.valid) return membershipResult\n\n return validatePurposeLegalBasisAlignment(cookiesMap, categoriesMap)\n}\n","import type { StorageAdapter, AuthConfig, CookieMap, CategoryMap } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport { validateProfileCompliance } from '../../services/compliance-validator.service'\nimport { validateConsentTemplate } from '../../validators/consent-template.validator'\n\nexport function buildAdminConsentTemplateRoutes(storage: StorageAdapter, authConfig: AuthConfig, secret: string, tenantId = 'default') {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /consent-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await storage.getConsentTemplates(tenantId))\n }),\n\n 'GET /consent-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const t = await storage.getConsentTemplate(params['id']!)\n return t ? json(200, t) : errorResponse(404, 'Consent template not found')\n }),\n\n 'POST /consent-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown; cookies?: unknown; categories?: unknown }>(req)\n if (!body?.name || typeof body.name !== 'string') return errorResponse(400, 'name is required')\n const templateResult = validateConsentTemplate(body.cookies, body.categories)\n if (!templateResult.valid) return errorResponse(400, templateResult.error!)\n const t = await storage.createConsentTemplate({\n tenantId,\n name: body.name,\n cookies: body.cookies as CookieMap,\n categories: body.categories as CategoryMap,\n })\n return json(201, t)\n }),\n\n 'PUT /consent-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const id = params['id']!\n const body = await parseJsonBody<{ name?: unknown; cookies?: unknown; categories?: unknown }>(req)\n\n const oldTemplate = await storage.getConsentTemplate(id)\n if (!oldTemplate) return errorResponse(404, 'Consent template not found')\n\n const update: { name?: string; cookies?: CookieMap; categories?: CategoryMap } = {}\n if (body?.name !== undefined && typeof body.name === 'string') update.name = body.name\n\n const cookiesChanged = body?.cookies !== undefined\n const categoriesChanged = body?.categories !== undefined\n const nextCookies = cookiesChanged ? body!.cookies : oldTemplate.cookies\n const nextCategories = categoriesChanged ? body!.categories : oldTemplate.categories\n\n if (cookiesChanged || categoriesChanged) {\n const templateResult = validateConsentTemplate(nextCookies, nextCategories)\n if (!templateResult.valid) return errorResponse(400, templateResult.error!)\n if (cookiesChanged) update.cookies = nextCookies as CookieMap\n if (categoriesChanged) update.categories = nextCategories as CategoryMap\n\n // Check whether this change breaks compliance for profiles currently using the template\n const profiles = await storage.findProfilesUsingConsentTemplate(id)\n const blockingProfiles: { id: string; name: string; complianceGroup: string }[] = []\n for (const p of profiles) {\n if (!p.complianceGroup) continue\n const result = validateProfileCompliance(nextCookies as CookieMap, nextCategories as CategoryMap, p.complianceGroup)\n if (!result.valid) blockingProfiles.push({ id: p.id, name: p.name, complianceGroup: p.complianceGroup })\n }\n if (blockingProfiles.length > 0) {\n return errorResponse(422, 'This change would break compliance for affected profiles', { blockingProfiles })\n }\n }\n\n const t = await storage.updateConsentTemplate(id, update)\n return json(200, t)\n }),\n\n 'DELETE /consent-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:delete')\n if (denied) return denied\n const id = params['id']!\n const profiles = await storage.findProfilesUsingConsentTemplate(id)\n if (profiles.length > 0) {\n return errorResponse(422, 'Cannot delete: template is used by profiles', {\n profiles: profiles.map(p => ({ id: p.id, name: p.name, isActive: p.isActive })),\n })\n }\n await storage.deleteConsentTemplate(id)\n return json(200, { ok: true })\n }),\n\n 'POST /consent-templates/:id/copy': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown }>(req)\n const src = await storage.getConsentTemplate(params['id']!)\n if (!src) return errorResponse(404, 'Consent template not found')\n const newName = (typeof body?.name === 'string' && body.name.trim()) ? body.name.trim() : `${src.name} (Copy)`\n const t = await storage.copyConsentTemplate(params['id']!, newName)\n return json(201, t)\n }),\n\n 'GET /consent-templates/:id/profile-usage': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const usage = await storage.findProfilesUsingConsentTemplate(params['id']!)\n return json(200, usage)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig } from '@consenti/types'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminUITemplateRoutes(storage: StorageAdapter, authConfig: AuthConfig, secret: string, tenantId = 'default') {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /ui-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n return json(200, await storage.getUITemplates(tenantId))\n }),\n\n 'GET /ui-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const t = await storage.getUITemplate(params['id']!)\n return t ? json(200, t) : errorResponse(404, 'UI template not found')\n }),\n\n 'POST /ui-templates': async (req: Request): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<Record<string, unknown>>(req)\n if (!body?.name || typeof body.name !== 'string') return errorResponse(400, 'name is required')\n if (!body.mainBanner || !body.gpcBanner || !body.preferenceModal) return errorResponse(400, 'mainBanner, gpcBanner, and preferenceModal are required')\n const t = await storage.createUITemplate({\n tenantId,\n name: body.name,\n mainBanner: body.mainBanner as never,\n gpcBanner: body.gpcBanner as never,\n preferenceModal: body.preferenceModal as never,\n })\n return json(201, t)\n }),\n\n 'PUT /ui-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:update')\n if (denied) return denied\n const body = await parseJsonBody<Record<string, unknown>>(req)\n const update: Record<string, unknown> = {}\n if (typeof body?.name === 'string') update['name'] = body.name\n if (body?.mainBanner) update['mainBanner'] = body.mainBanner\n if (body?.gpcBanner) update['gpcBanner'] = body.gpcBanner\n if (body?.preferenceModal) update['preferenceModal'] = body.preferenceModal\n const t = await storage.updateUITemplate(params['id']!, update as never)\n return json(200, t)\n }),\n\n 'DELETE /ui-templates/:id': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:delete')\n if (denied) return denied\n const id = params['id']!\n const profiles = await storage.findProfilesUsingUITemplate(id)\n if (profiles.length > 0) {\n return errorResponse(422, 'Cannot delete: template is used by profiles', {\n profiles: profiles.map(p => ({ id: p.id, name: p.name, isActive: p.isActive })),\n })\n }\n await storage.deleteUITemplate(id)\n return json(200, { ok: true })\n }),\n\n 'POST /ui-templates/:id/copy': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:create')\n if (denied) return denied\n const body = await parseJsonBody<{ name?: unknown }>(req)\n const src = await storage.getUITemplate(params['id']!)\n if (!src) return errorResponse(404, 'UI template not found')\n const newName = (typeof body?.name === 'string' && body.name.trim()) ? body.name.trim() : `${src.name} (Copy)`\n const t = await storage.copyUITemplate(params['id']!, newName)\n return json(201, t)\n }),\n\n 'GET /ui-templates/:id/profile-usage': async (req: Request, params: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'profile:view')\n if (denied) return denied\n const usage = await storage.findProfilesUsingUITemplate(params['id']!)\n return json(200, usage)\n }),\n }\n}\n","import type { StorageAdapter, AuthConfig, OptInFilters } from '@consenti/types'\nimport { json } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\n\nexport function buildAdminAnalyticsRoutes(\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n defaultTenantId = 'default',\n) {\n async function auth(req: Request, perm?: string) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { user, denied: authError(user, perm) }\n }\n\n return {\n 'GET /analytics/opt-in': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req, 'stats:view')\n if (denied) return denied\n\n const url = new URL(req.url)\n const tenantId = url.searchParams.get('tenantId') ?? defaultTenantId\n\n const filters: OptInFilters = {}\n const profileId = url.searchParams.get('profileId')\n const complianceGroup = url.searchParams.get('complianceGroup')\n const from = url.searchParams.get('from')\n const to = url.searchParams.get('to')\n const locale = url.searchParams.get('locale')\n\n if (profileId) filters.profileId = profileId\n if (complianceGroup) filters.complianceGroup = complianceGroup\n if (from) filters.from = from\n if (to) filters.to = to\n if (locale) filters.locale = locale\n\n const stats = await storage.getOptInStats(tenantId, filters)\n return json(200, stats)\n }),\n }\n}\n","import type { ConsentiServerConfig } from '@consenti/types'\n\nconst REDACTED = '••••••••'\n\n/**\n * Builds a display-safe copy of the fully-merged server config for the setup wizard's\n * \"resolved configuration\" step. Allowlist-based, not a denylist — every field shown here is\n * named explicitly, so a future secret-bearing field added to `ConsentiServerConfig` is hidden\n * by default instead of leaking until someone remembers to redact it. Function-valued fields\n * (`validateUser`, `handleCache`, a custom `geoDataProvider`/`autoComplianceMap`) aren't\n * meaningful to display and are omitted rather than serialized.\n */\nexport function sanitizeConfigForDisplay(config: ConsentiServerConfig): Record<string, unknown> {\n const storage = config.storage\n const auth = config.auth\n const compliance = config.compliance\n const dashboard = config.dashboard\n\n return {\n basePath: config.basePath,\n dashboard: typeof dashboard === 'object'\n ? { enabled: dashboard.enabled, path: dashboard.path }\n : dashboard,\n ...(storage ? {\n storage: {\n driver: storage.driver,\n ...(storage.path !== undefined ? { path: storage.path } : {}),\n ...(storage.uri !== undefined ? { uri: REDACTED } : {}),\n ...(storage.database !== undefined ? { database: storage.database } : {}),\n ...(storage.host !== undefined ? { host: storage.host } : {}),\n ...(storage.port !== undefined ? { port: storage.port } : {}),\n ...(storage.user !== undefined ? { user: storage.user } : {}),\n ...(storage.password !== undefined ? { password: REDACTED } : {}),\n ...(storage.poolMax !== undefined ? { poolMax: storage.poolMax } : {}),\n ...(storage.statementTimeoutMs !== undefined ? { statementTimeoutMs: storage.statementTimeoutMs } : {}),\n ...(storage.idleInTransactionTimeoutMs !== undefined ? { idleInTransactionTimeoutMs: storage.idleInTransactionTimeoutMs } : {}),\n },\n } : {}),\n ...(auth ? {\n auth: {\n mode: auth.mode,\n ...(auth.adminEmail !== undefined ? { adminEmail: auth.adminEmail } : {}),\n ...(auth.adminPassword !== undefined ? { adminPassword: REDACTED } : {}),\n ...(auth.jwtSecret !== undefined ? { jwtSecret: REDACTED } : {}),\n ...(auth.oidc ? {\n oidc: { issuer: auth.oidc.issuer, clientId: auth.oidc.clientId, clientSecret: REDACTED, redirectUri: auth.oidc.redirectUri },\n } : {}),\n ...(auth.saml ? {\n saml: { issuer: auth.saml.issuer, entryPoint: auth.saml.entryPoint, cert: REDACTED, callbackUrl: auth.saml.callbackUrl },\n } : {}),\n },\n } : {}),\n ...(config.rateLimit ? { rateLimit: config.rateLimit } : {}),\n ...(compliance ? {\n compliance: {\n ...(compliance.type !== undefined ? { type: compliance.type } : {}),\n ...(compliance.gpc !== undefined ? { gpc: compliance.gpc } : {}),\n ...(compliance.geoDataProvider !== undefined ? {\n geoDataProvider: typeof compliance.geoDataProvider === 'function' ? '<custom function>' : compliance.geoDataProvider,\n } : {}),\n ...(compliance.complianceMapUrl !== undefined ? { complianceMapUrl: compliance.complianceMapUrl } : {}),\n },\n } : {}),\n ...(config.multiTenant ? { multiTenant: config.multiTenant } : {}),\n ...(config.plugins ? { plugins: { count: config.plugins.length } } : {}),\n ...(config.ageGate ? { ageGate: config.ageGate } : {}),\n ...(config.tcf ? { tcf: config.tcf } : {}),\n ...(config.dataRetention ? { dataRetention: config.dataRetention } : {}),\n ...(config.maxBodySize !== undefined ? { maxBodySize: config.maxBodySize } : {}),\n ...(config.trustedProxies ? { trustedProxies: config.trustedProxies } : {}),\n ...(config.branding ? { branding: config.branding } : {}),\n ...(config.s3Api ? {\n s3Api: { enabled: config.s3Api.enabled, region: config.s3Api.region, bucketName: config.s3Api.bucketName, accessKeyId: REDACTED, secretAccessKey: REDACTED },\n } : {}),\n ...(config.consentSigningKey !== undefined ? { consentSigningKey: REDACTED } : {}),\n }\n}\n","import type { StorageAdapter, AuthConfig, ConsentiServerConfig } from '@consenti/types'\nimport { COMPLIANCE_GROUP_IDS, COMPLIANCE_GROUPS } from '@consenti/utils'\nimport { json, parseJsonBody } from '../../utils/http'\nimport { errorResponse, withErrorHandler } from '../../middleware/error.middleware'\nimport { authenticate, authError } from '../../middleware/auth.middleware'\nimport { sanitizeConfigForDisplay } from '../../utils/sanitize-config'\nimport type { ProfileService } from '../../services/profile.service'\n\n/** First-run setup wizard — one time per tenant, gated by `tenant_settings.setup_completed`.\n * Never reset from the dashboard once true (see plans/DONE-api-setup-wizard.md). */\nexport function buildAdminSetupRoutes(\n profileService: ProfileService,\n storage: StorageAdapter,\n authConfig: AuthConfig,\n secret: string,\n config: ConsentiServerConfig,\n readiness: { usingJsonStorage: boolean; usingDefaultCredentials: boolean },\n) {\n async function auth(req: Request) {\n const user = await authenticate(req, storage, authConfig, secret)\n return { denied: authError(user, 'settings:update') }\n }\n\n /** Blocks the two mutating setup routes once setup is already complete — the wizard is a\n * one-time installer, not something callable again via a direct API request either. */\n async function rejectIfCompleted(): Promise<Response | null> {\n const settings = await storage.getSettings('default')\n if (settings.setupCompleted === true) return errorResponse(409, 'Setup has already been completed')\n return null\n }\n\n return {\n 'GET /setup/status': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const settings = await storage.getSettings('default')\n return json(200, { completed: settings.setupCompleted === true })\n }),\n\n 'GET /setup/config': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n return json(200, { config: sanitizeConfigForDisplay(config), ...readiness })\n }),\n\n 'GET /setup/compliance-groups': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const groups = COMPLIANCE_GROUP_IDS.map(id => ({ id, ...COMPLIANCE_GROUPS[id] }))\n return json(200, groups)\n }),\n\n 'POST /setup/seed-profiles': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const completed = await rejectIfCompleted()\n if (completed) return completed\n const body = await parseJsonBody(req)\n const groups = (body as { groups?: unknown } | null)?.groups\n if (!Array.isArray(groups) || groups.some(g => typeof g !== 'string')) {\n return errorResponse(400, 'groups must be an array of compliance group ids')\n }\n const unknown = groups.filter(g => !(COMPLIANCE_GROUP_IDS as readonly string[]).includes(g))\n if (unknown.length > 0) {\n return errorResponse(400, `Unknown compliance group id(s): ${unknown.join(', ')}`)\n }\n await Promise.all((groups as string[]).map(g => profileService.seedDefaultProfile(g)))\n return json(200, { seeded: groups })\n }),\n\n 'POST /setup/complete': async (req: Request, _p: Record<string, string>): Promise<Response> =>\n withErrorHandler(async () => {\n const { denied } = await auth(req)\n if (denied) return denied\n const completed = await rejectIfCompleted()\n if (completed) return completed\n const settings = await storage.updateSettings('default', { setupCompleted: true })\n return json(200, settings)\n }),\n }\n}\n","import { createHash } from 'node:crypto'\nimport type { StorageAdapter } from '@consenti/types'\n\nexport async function resolveTenant(\n req: Request,\n storage: StorageAdapter,\n defaultTenantId: string,\n): Promise<string> {\n const auth = req.headers.get('authorization')\n if (!auth?.startsWith('Bearer ck_')) return defaultTenantId\n\n const rawKey = auth.slice(7)\n const keyHash = createHash('sha256').update(rawKey).digest('hex')\n const apiKey = await storage.getApiKeyByHash(keyHash)\n if (!apiKey || !apiKey.isActive) return defaultTenantId\n\n if (apiKey.expireBy && new Date(apiKey.expireBy) <= new Date()) {\n // Lazy expiration: no background sweep — the first request (or dashboard list load) to\n // notice an expired key flips it, so it reads correctly from then on.\n await storage.revokeApiKey(apiKey.id).catch(() => {})\n return defaultTenantId\n }\n\n return apiKey.tenantId\n}\n","interface RateLimitEntry {\n count: number\n resetAt: number\n}\n\nexport class RateLimiter {\n private store = new Map<string, RateLimitEntry>()\n\n constructor(\n private windowMs: number = 60_000,\n private maxRequests: number = 60,\n ) {\n setInterval(() => {\n const now = Date.now()\n for (const [key, entry] of this.store) {\n if (now >= entry.resetAt) this.store.delete(key)\n }\n }, this.windowMs).unref()\n }\n\n check(key: string): boolean {\n const now = Date.now()\n const entry = this.store.get(key)\n\n if (!entry || now >= entry.resetAt) {\n this.store.set(key, { count: 1, resetAt: now + this.windowMs })\n return true\n }\n\n entry.count += 1\n return entry.count <= this.maxRequests\n }\n\n // Returns false if either the IP bucket or the profileId bucket is exhausted.\n checkRequest(ip: string, profileId?: string): boolean {\n const ipOk = this.check(`ip:${ip}`)\n if (!ipOk) return false\n if (profileId) return this.check(`profile:${profileId}`)\n return true\n }\n}\n","/**\n * ConsentAction — server-side counterpart to `@consenti/ui`'s `ConsentAction`.\n *\n * Watches a single consent parameter across *every* visitor's submission (not just one\n * browser's own state) and fires `onGrant`/`onDeny` when that parameter's status actually\n * changes — via the same `eventBus` `createConsenti()` already emits `consent.created`/\n * `consent.updated` on. Useful for server-side integrations (CRM sync, suppression lists,\n * downstream webhooks) that need to react to consent decisions without polling the database.\n *\n * There is no server equivalent of the widget's `ConsentScript` — injecting a `<script>` tag\n * only makes sense in a browser DOM the server doesn't have. `services.consent.create()`/\n * `.update()` are already the server-side \"submit\"/\"update consent\" methods this hook reacts to.\n *\n * @example\n * ```ts\n * import { createConsenti, ConsentAction } from '@consenti/api'\n *\n * const { eventBus } = createConsenti({ ... })\n *\n * new ConsentAction({\n * id: 'analytics_storage',\n * eventBus,\n * onGrant: ({ visitorId }) => crm.optIn(visitorId),\n * onDeny: ({ visitorId }) => crm.optOut(visitorId),\n * })\n * ```\n */\n\nimport type { EventEmitter } from 'node:events'\nimport type { ConsentDbRecord, ConsentStatus } from '@consenti/types'\n\n/** Passed to `onGrant`/`onDeny` — the transitioning parameter's status plus the full consent record. */\nexport interface ConsentActionParams {\n visitorId: string\n cookieId: string\n status: ConsentStatus\n record: ConsentDbRecord\n}\n\n/** Options for constructing a {@link ConsentAction} instance. */\nexport interface ConsentActionOptions {\n /** The consent parameter ID to watch (e.g. `'analytics_storage'`). */\n id: string\n /** The `eventBus` returned by `createConsenti()`. */\n eventBus: EventEmitter\n /** Called when the parameter's status transitions to `'granted'`, for any visitor. */\n onGrant?: (params: ConsentActionParams) => void\n /** Called when the parameter's status transitions away from `'granted'` (`'denied'` or `'objected'`). */\n onDeny?: (params: ConsentActionParams) => void\n}\n\n/**\n * Watches a single consent parameter across every incoming `consent.created`/`consent.updated`\n * event and fires `onGrant`/`onDeny` on transitions. Call {@link ConsentAction.destroy} to\n * remove the event listeners when the hook is no longer needed.\n */\nexport class ConsentAction {\n private readonly onCreated = (record: ConsentDbRecord): void => this.fire(record, undefined)\n private readonly onUpdated = ({ previous, current }: { previous: ConsentDbRecord; current: ConsentDbRecord }): void =>\n this.fire(current, previous)\n\n constructor(private readonly options: ConsentActionOptions) {\n options.eventBus.on('consent.created', this.onCreated)\n options.eventBus.on('consent.updated', this.onUpdated)\n }\n\n private fire(record: ConsentDbRecord, previous: ConsentDbRecord | undefined): void {\n const status: ConsentStatus = record.consentJson[this.options.id] ?? 'denied'\n const prevStatus: ConsentStatus = previous?.consentJson[this.options.id] ?? 'denied'\n if (status === prevStatus) return\n\n const params: ConsentActionParams = { visitorId: record.visitorId, cookieId: this.options.id, status, record }\n if (status === 'granted') this.options.onGrant?.(params)\n else this.options.onDeny?.(params)\n }\n\n /** Removes the event listeners. Call when the hook is no longer needed. */\n destroy(): void {\n this.options.eventBus.off('consent.created', this.onCreated)\n this.options.eventBus.off('consent.updated', this.onUpdated)\n }\n}\n","/**\n * CategoryAction — server-side counterpart to `@consenti/ui`'s `CategoryAction`.\n *\n * Same idea as {@link ConsentAction} but watches a whole category's rollup consent state\n * (granted only when *every* cookie in the category is granted) instead of one parameter —\n * matching the preference modal's tri-state rollup rule and the widget's `CategoryAction`.\n *\n * @example\n * ```ts\n * import { createConsenti, CategoryAction } from '@consenti/api'\n *\n * const { eventBus, services } = createConsenti({ ... })\n *\n * new CategoryAction({\n * categoryId: 'marketing',\n * eventBus,\n * profiles: services.profile,\n * onGrant: ({ visitorId }) => adsPlatform.optIn(visitorId),\n * onDeny: ({ visitorId }) => adsPlatform.optOut(visitorId),\n * })\n * ```\n */\n\nimport type { EventEmitter } from 'node:events'\nimport type { ConsentDbRecord, ConsentStatus, ConsentValue } from '@consenti/types'\nimport type { ProfileService } from '../services/profile.service'\n\n/** Passed to `onGrant`/`onDeny` — the category's rollup status plus the full consent record. */\nexport interface CategoryActionParams {\n visitorId: string\n categoryId: string\n status: ConsentStatus\n record: ConsentDbRecord\n}\n\n/** Options for constructing a {@link CategoryAction} instance. */\nexport interface CategoryActionOptions {\n /** The category ID to watch (e.g. `'marketing'`). */\n categoryId: string\n /** The `eventBus` returned by `createConsenti()`. */\n eventBus: EventEmitter\n /** The `services.profile` returned by `createConsenti()` — resolves which cookies belong to\n * this category for the record's profile (categories are defined on the consent template). */\n profiles: ProfileService\n /** Called when every cookie in the category transitions to `'granted'`. */\n onGrant?: (params: CategoryActionParams) => void\n /** Called when the category transitions away from fully granted. */\n onDeny?: (params: CategoryActionParams) => void\n}\n\nfunction isCategoryGranted(consent: ConsentValue, cookieIds: string[]): boolean {\n return cookieIds.length > 0 && cookieIds.every(id => consent[id] === 'granted')\n}\n\n/**\n * Watches a category's rollup consent status across every incoming `consent.created`/\n * `consent.updated` event and fires `onGrant`/`onDeny` on transitions. Call\n * {@link CategoryAction.destroy} to remove the event listeners when no longer needed.\n */\nexport class CategoryAction {\n private readonly onCreated = (record: ConsentDbRecord): void => { void this.fire(record, undefined) }\n private readonly onUpdated = ({ previous, current }: { previous: ConsentDbRecord; current: ConsentDbRecord }): void => {\n void this.fire(current, previous)\n }\n\n constructor(private readonly options: CategoryActionOptions) {\n options.eventBus.on('consent.created', this.onCreated)\n options.eventBus.on('consent.updated', this.onUpdated)\n }\n\n private async fire(record: ConsentDbRecord, previous: ConsentDbRecord | undefined): Promise<void> {\n const resolved = await this.options.profiles.getResolved(record.profileId)\n const cookieIds = resolved?.preferenceModal.categories[this.options.categoryId]?.cookies ?? []\n if (cookieIds.length === 0) return\n\n const status: ConsentStatus = isCategoryGranted(record.consentJson, cookieIds) ? 'granted' : 'denied'\n const prevStatus: ConsentStatus = previous && isCategoryGranted(previous.consentJson, cookieIds) ? 'granted' : 'denied'\n if (status === prevStatus) return\n\n const params: CategoryActionParams = { visitorId: record.visitorId, categoryId: this.options.categoryId, status, record }\n if (status === 'granted') this.options.onGrant?.(params)\n else this.options.onDeny?.(params)\n }\n\n /** Removes the event listeners. Call when the hook is no longer needed. */\n destroy(): void {\n this.options.eventBus.off('consent.created', this.onCreated)\n this.options.eventBus.off('consent.updated', this.onUpdated)\n }\n}\n"],"mappings":"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","names":["EventEmitter","readFile","join","extname","dirname","fileURLToPath","PluginEngine","plugins","context","plugin","err","data","result","record","profile","user","copyFileSync","existsSync","mkdirSync","extname","join","buildConsentiRuntimeConfig","dashboardDir","basePath","branding","compliance","authMode","resolvedCompliance","buildConsentiConfigScript","cfg","injectConfigScript","html","script","str","GVL_URL","cachedGvl","lastFetched","refreshTimer","isGvlData","d","getGvl","res","GVL_URL","data","startGvlRefresh","stopGvlRefresh","SHARED_SCHEMAS","OPENAPI_PUBLIC_SPEC","OPENAPI_ADMIN_SPEC","createHash","createHmac","randomUUID","scryptSync","randomBytes","timingSafeEqual","BASE62","ID_SUFFIX_LEN","randomBase62","length","bytes","randomBytes","out","i","prefixedId","prefix","randomProfileId","randomVisitorId","randomConsentId","randomConsentTemplateId","randomUITemplateId","hashIp","ip","createHash","hashUserAgent","ua","signHmac","payload","secret","createHmac","verifyHmac","signature","expected","actual","timingSafeEqual","hashPassword","password","salt","hash","scryptSync","verifyPassword","stored","derived","signJwt","expiresInSeconds","now","header","body","sig","h","sigEncoded","verifyJwt","token","parts","sigBytes","actualHex","c","expectedBuf","actualBuf","exp","existsSync","accessSync","constants","mkdirSync","resolve","extname","dirname","join","fileURLToPath","DB_FILE_NAMES","_thisDir","isInsidePackageInstall","resolved","nodeModulesSegment","isInDist","resolveStoragePaths","rawPath","driver","basePath","ext","dbDir","profilesDir","logsDir","dbFileName","dbPath","tenants","profiles","consentRecords","consentHistory","visitors","noticeShown","users","usersSqlite","roles","permissions","userRoles","rolePermissions","auditLogs","apiKeys","tenantSettings","consentTemplates","uiTemplates","ALL_TABLES_SQLITE","ALL_TABLES_FULL","sqliteDef","def","buildColSQLite","c","type","parts","buildTableSQLite","t","lines","u","idxName","cols","buildIndexesSQLite","i","pgDef","buildColPg","buildTablePg","buildIndexesPg","MYSQL_TEXT","mysqlDef","buildColMySQL","buildTableMySQL","buildIndexesMySQL","toSQLite","tables","idx","toPostgres","toMySQL","collectIndexes","result","ALL_INDEXES","SCHEMA_SQL_SQLITE","SCHEMA_SQL_POSTGRES","SCHEMA_SQL_MYSQL","SEED_TENANT","SEED_PERMISSIONS","SEED_ROLES","adminExcluded","complianceAllowed","viewerAllowed","SEED_ROLE_PERMISSIONS","p","q","s","generateSeedSQL","id","name","slug","tenant","perms","rolesSQL","r","byRole","rp","rolePerms","roleId","permIds","pid","SEED_SQL","likePrefix","input","ch","MAX_PAGE_SIZE","BetterSqlite3Adapter","_BetterSqlite3Adapter","config","sql","s","mod","dbPath","resolveStoragePaths","SCHEMA_SQL_SQLITE","SEED_SQL","row","data","id","randomProfileId","now","profile","fields","vals","updated","tenantId","r","complianceGroup","randomConsentId","histId","randomUUID","record","visitorId","existing","filters","conditions","like","likePrefix","where","page","limit","offset","rows","countRow","randomVisitorId","visitor","allowedTenants","allowedTenantsJson","user","email","roleId","permissionId","userId","pageSize","tc","gc","vc","totalConsents","recent","accepted","rejected","grantedPct","v","sample","stats","consent","key","status","days","total","detected","t","d","merged","olderThanDays","cutoff","toDelete","visitor_id","count","keyHash","randomConsentTemplateId","newName","src","randomUITemplateId","name","settings","_n","settingsUpdate","mainBanner","gpcBanner","preferenceModal","templateId","granted","denied","managed","byLocale","statuses","deniedPct","loc","dateRows","dateMap","entry","byDate","date","MAX_PAGE_SIZE","NodeSqlite3WasmAdapter","_NodeSqlite3WasmAdapter","config","sql","s","mod","dbPath","resolveStoragePaths","stmt","SCHEMA_SQL_SQLITE","SEED_SQL","row","data","id","randomProfileId","now","profile","fields","vals","updated","tenantId","r","complianceGroup","p","randomConsentId","histId","randomUUID","record","visitorId","existing","filters","conditions","like","likePrefix","where","page","limit","offset","rows","countRow","randomVisitorId","visitor","allowedTenants","allowedTenantsJson","user","email","roleId","permissionId","userId","pageSize","tc","gc","vc","totalConsents","recent","accepted","rejected","grantedPct","v","sample","stats","consent","key","status","days","total","detected","t","d","merged","olderThanDays","cutoff","toDelete","visitor_id","count","keyHash","randomConsentTemplateId","newName","src","randomUITemplateId","name","settings","_n","settingsUpdate","mainBanner","gpcBanner","preferenceModal","templateId","granted","denied","managed","byLocale","statuses","deniedPct","loc","dateRows","dateMap","entry","byDate","date","MAX_PAGE_SIZE","NodeSqliteBuiltinAdapter","_NodeSqliteBuiltinAdapter","config","sql","s","Db","dbPath","resolveStoragePaths","SCHEMA_SQL_SQLITE","SEED_SQL","row","data","id","randomProfileId","now","profile","fields","vals","updated","tenantId","r","complianceGroup","randomConsentId","histId","randomUUID","record","visitorId","existing","filters","conditions","like","likePrefix","where","page","limit","offset","rows","countRow","randomVisitorId","visitor","allowedTenants","allowedTenantsJson","user","email","roleId","permissionId","userId","pageSize","tc","gc","vc","totalConsents","recent","accepted","rejected","grantedPct","v","sample","stats","consent","key","status","days","total","detected","t","d","merged","olderThanDays","cutoff","toDelete","visitor_id","count","keyHash","randomConsentTemplateId","newName","src","randomUITemplateId","name","settings","_n","settingsUpdate","mainBanner","gpcBanner","preferenceModal","templateId","granted","denied","managed","byLocale","statuses","grantedCount","deniedCount","loc","dateRows","dateMap","entry","deniedPct","byDate","date","escapeRegex","s","mapProfile","d","mapConsent","CONSENT_SUMMARY_PROJECTION","mapConsentSummary","mapHistory","mapVisitor","mapUser","mapRole","mapPermission","mapAuditLog","AUDIT_SUMMARY_PROJECTION","mapAuditSummary","MongoDBAdapter","config","name","MC","uri","table","cols","unique","ALL_INDEXES","spec","c","id","slug","SEED_TENANT","p","SEED_PERMISSIONS","doc","SEED_ROLES","rp","roleId","permissionId","SEED_ROLE_PERMISSIONS","data","now","randomProfileId","set","res","tenantId","complianceGroup","randomConsentId","visitorId","existing","filters","q","page","limit","docsRaw","total","cursor","f","range","re","consentRecordId","oldJson","newJson","action","randomUUID","randomVisitorId","r","gpcCount","visitors","pipeline","results","granted","denied","all","rows","result","cookie","entry","days","from","email","ids","userId","filter","detected","keyHash","expired","update","olderThanDays","cutoff","docs","visitorIds","count","mapProfile","r","mapConsent","CONSENT_SUMMARY_COLS","mapConsentSummary","mapHistory","mapVisitor","mapUser","mapRole","mapPermission","mapAuditLog","AUDIT_SUMMARY_COLS","mapAuditSummary","MySQLAdapter","config","sql","params","timeout","rows","result","mysql","cfg","conn","tables","stmt","SCHEMA_SQL_MYSQL","SEED_TENANT","p","SEED_PERMISSIONS","SEED_ROLES","roleId","permissionId","SEED_ROLE_PERMISSIONS","data","id","randomProfileId","row","sets","vals","tenantId","complianceGroup","randomConsentId","visitorId","existing","filters","where","page","limit","countRows","f","like","likePrefix","consentRecordId","oldJson","newJson","action","randomUUID","randomVisitorId","countRow","gpcRow","visitorRow","total","gpcCount","visitors","statusRows","granted","denied","all","entry","days","from","email","userId","d","detected","keyHash","now","merged","olderThanDays","cutoff","visitorRows","ids","placeholders","mapProfile","r","mapConsent","CONSENT_SUMMARY_COLS","mapConsentSummary","mapHistory","mapVisitor","mapUser","mapRole","mapPermission","mapAuditLog","AUDIT_SUMMARY_COLS","mapAuditSummary","PostgreSQLAdapter","config","sql","params","PgPool","cfg","poolCfg","client","version","stmt","SCHEMA_SQL_POSTGRES","err","SEED_TENANT","p","SEED_PERMISSIONS","SEED_ROLES","roleId","permissionId","SEED_ROLE_PERMISSIONS","data","id","randomProfileId","row","sets","vals","idx","rows","tenantId","complianceGroup","randomConsentId","visitorId","existing","filters","where","nextIdx","page","limit","countRows","f","likePrefix","consentRecordId","oldJson","newJson","action","randomUUID","randomVisitorId","countRow","gpcRow","visitorRow","total","gpcCount","visitors","statusRows","granted","denied","all","result","entry","days","from","email","userId","d","detected","keyHash","now","merged","olderThanDays","cutoff","visitorRows","ids","placeholders","_","i","readFile","writeFile","rename","mkdir","randomUUID","MAX_PAGE_SIZE","emptyDb","page","arr","pageNum","limit","l","offset","toConsentSummary","c","id","tenantId","visitorId","profileId","locale","gpcDetected","source","ageVerified","createdAt","updatedAt","toAuditSummary","userId","action","resourceType","resourceId","JsonFileAdapter","config","mkdir","tmp","writeFile","rename","err","dbPath","resolveStoragePaths","raw","readFile","now","name","slug","SEED_TENANT","t","p","SEED_PERMISSIONS","x","SEED_ROLES","rp","SEED_ROLE_PERMISSIONS","data","profile","randomProfileId","idx","updated","a","b","complianceGroup","randomConsentId","record","randomUUID","existing","h","filters","rows","q","page_","n","visitor","randomVisitorId","v","user","u","ur","email","r","role","roleId","permIds","urs","roleIds","permissionId","log","consents","recent","accepted","rejected","vals","grantedPct","sample","stats","key","status","days","cutoff","counts","date","count","consentVisitors","country","detected","total","tenant","olderThanDays","toDelete","before","removed","keyHash","k","tpl","randomConsentTemplateId","newName","src","settings","randomUITemplateId","_name","mainBanner","gpcBanner","preferenceModal","json","ct","ut","templateId","records","granted","denied","managed","byLocale","dateMap","statuses","s","deniedPct","loc","entry","byDate","ProfileRepo","adapter","data","id","tenantId","complianceGroup","ConsentRepo","adapter","data","visitorId","filters","VisitorRepo","adapter","data","visitorId","NoticeRepo","adapter","data","visitorId","AuditRepo","adapter","data","filters","id","TTLCache","ttlMs","maxEntries","sweepIntervalMs","key","entry","value","oldest","now","STATS_CACHE_TTL_MS","StatsService","storage","TTLCache","tenantId","days","key","load","hit","value","UserRepo","adapter","id","email","tenantId","data","userId","roleId","readdirSync","readFileSync","existsSync","statSync","lstatSync","join","Worker","fileURLToPath","join","dirname","existsSync","writeFileSync","renameSync","mkdirSync","copyFileSync","rmSync","existsSync","readFileSync","lstatSync","symlinkSync","unlinkSync","join","resolve","randomUUID","createHmac","createHash","sha256Hex","data","hmacSha256","key","toAmzDate","d","iso","getSigningKey","secretKey","dateStamp","region","service","kDate","kRegion","kService","buildSignedHeaders","params","method","body","contentType","config","now","amzDate","host","payloadHash","signedHeaderMap","sortedKeys","canonicalHeaders","k","signedHeadersList","canonicalRequest","credentialScope","stringToSign","signingKey","signature","authorization","s3Url","encodedKey","s3Put","headers","bodyStr","res","text","s3Delete","key","config","headers","buildSignedHeaders","res","s3Url","text","profilesRoot","storagePath","tenantId","join","versionDir","profileId","version","complianceDir","complianceGroup","s3Key","segment","locale","writeJsonFile","filePath","content","tmp","writeFileSync","renameSync","PROFILE_WIDE_KEYS","profileWideFields","profile","result","key","value","writeVersionDir","msg","localeContent","previousVersion","s3Config","dir","mkdirSync","locales","defaultLocale","cookies","expiryDays","resolved","doc","prevPath","existsSync","readFileSync","s3Put","fallbackLocale","fallbackPath","copyFileSync","clearNonSymlink","linkPath","lstatSync","rmSync","activateComplianceGroup","srcDir","resolve","tmpLinkPath","randomUUID","symlinkSync","pointer","deactivateComplianceGroup","unlinkSync","s3Delete","runWorkerAction","_dir","dirname","fileURLToPath","JS_WORKER","join","IS_DEV","existsSync","runProfileJsonWrite","msg","runWorkerAction","resolve","reject","worker","Worker","result","err","applyCookiesOverride","cookies","override","result","id","delta","profileWideFields","pj","ProfileService","profiles","audit","tenantId","storage","eventBus","localeCache","profilesDir","handleCache","s3Config","profileId","join","complianceGroup","locales","dir","l","ct","profile","localeContent","previousVersion","allLocaleContent","isActive","msg","runProfileJsonWrite","paths","input","rest","name","src","locale","raw","parsed","old","carriedIsActive","profileJson","groupKey","updated","currentLocale","cached","content","response","err","existsSync","versions","entries","readdirSync","entry","version","vdir","f","createdAt","statSync","a","b","filePath","defaultPath","readFileSync","root","existingIds","p","results","dirPath","lstatSync","versionDirs","v","lastModified","mtime","DEFAULT_PROFILES","resolveLocaleTranslation","embedded","enTranslation","otherLocales","lang","resolved","randomUUID","COMPLIANCE_GROUP_IDS","g","TIMEZONE_TO_COUNTRY","LANG_TO_COUNTRY","GeoResolverService","provider","map","ctx","country","region","regulation","locale","result","resolveFromTimezone","resolveFromLanguage","resolveFromGeoIPLite","resolveFromHosted","resolveFromMaxmind","geo","entry","language","parts","tag","timezone","TIMEZONE_TO_COUNTRY","ip","r","reg","maxmind","dbPath","resolve","https","url","req","res","chunks","c","body","existsSync","readFileSync","readdirSync","unlinkSync","join","copyFileSync","writeFileSync","renameSync","unlinkSync","readFileSync","existsSync","mkdirSync","dirname","safeJsonWrite","filePath","data","validateSchema","bak","tmp","had","parsed","err","isPublicProfileResponse","v","LocaleJsonCacheService","dir","profileId","locale","path","existsSync","parsed","readFileSync","response","safeJsonWrite","responses","err","prefix","file","readdirSync","unlinkSync","join","validateConsentInput","data","cookies","categoryIndex","errors","cookieId","status","legalBasis","getCookieLegalBasis","buildConsentValue","submitted","gpcDetected","regulation","result","id","cookie","isMandatoryCookie","buildConsentSignaturePayload","record","sortedConsent","k","verifyConsent","recordProfile","activeProfile","signingKey","reasons","isExpired","signHmac","buildTcfPayload","consentValue","vendorSet","purposeSet","p","a","b","profile","expiryDays","expiryMs","ConsentService","consents","visitors","profiles","audit","tenantId","compliance","pluginEngine","eventBus","tcfConfig","storage","consentSigningKey","payload","signHmac","buildConsentSignaturePayload","pj","needsCookies","needsCategories","ct","applyCookiesOverride","input","profile","regulation","profileCookies","categories","categoryIndex","buildCookieCategoryIndex","effectiveConsent","id","cookie","validation","validateConsentInput","builtConsent","buildConsentValue","tcfString","c","gvl","getGvl","vendorConsents","purposeConsents","buildTcfPayload","encodeTcString","signature","baseInput","existing","record","updateData","visitorId","profileCookies2","categories2","categoryIndex2","effectiveGpc","updatePayload","recordProfile","complianceGroup","activeProfile","verifyConsent","VisitorService","visitors","tenantId","eventBus","data","input","hashIp","hashUserAgent","visitor","NoticeService","notices","tenantId","data","visitorId","UserService","users","audit","tenantId","user","_p","rest","input","actorId","hashPassword","id","existing","data","updated","u","userId","roleId","LOCKOUT_WINDOW_MS","MAX_FAILED_ATTEMPTS","SESSION_TTL_SECONDS","LocalAuth","storage","secret","now","key","entry","email","password","passwordHash","hashPassword","user","attempts","verifyPassword","roles","permSets","r","permissions","p","signJwt","payload","token","verifyJwt","id","PayloadTooLargeError","getIp","req","trustedProxies","remoteIp","fwd","json","status","data","parseJsonBody","request","getQueryParam","url","key","getQueryInt","fallback","val","n","MAX_LIST_PAGE_SIZE","getPagination","defaultLimit","limit","nodeRequestToFetch","maxBodyBytes","proto","host","headers","value","v","body","method","chunks","totalBytes","chunk","fetchResponseToNode","response","res","setCookies","cookie","reader","done","errorResponse","status","message","details","body","withErrorHandler","fn","err","existsSync","readFileSync","join","CACHE_CONTROL","JSON_HEADERS","serveJsonFile","filePath","content","redirectToDefault","basePath","tenantId","complianceGroup","buildProfileRoutes","profiles","geoResolver","profilesDir","_req","params","withErrorHandler","complianceGroupOrProfileId","locale","errorResponse","defaultPath","result","json","profileId","entryId","request","_params","url","ip","timezone","language","requestedLocale","dataParam","decoded","geo","profile","availableLocales","resolvedLocale","langPrefix","prefixMatch","l","found","servePath","_request","id","sameSiteAttrs","ownershipToken","visitorId","consentId","secret","sig","signHmac","buildOwnershipSetCookie","name","value","buildExpireOwnershipCookie","verifyOwnershipToken","token","parts","verifyHmac","patternHostname","pattern","matchesAllowedOrigin","origin","allowedOrigins","hostname","suffix","buildCorsHeaders","allowOrigin","handleCors","request","withCors","response","cors","newHeaders","k","v","checkOriginAllowed","CSP","addSecurityHeaders","res","h","VALID_STATUSES","validateConsentJsonValues","consentJson","entries","key","val","validateCreateConsent","body","b","validateUpdateConsent","castCreateConsent","visitorId","tenantId","castUpdateConsent","result","assertVisitorOwnership","request","visitorId","secret","cookieHeader","name","c","trimmed","eqIdx","verifyOwnershipToken","errorResponse","buildConsentRoutes","consents","visitors","profiles","resolveTenantId","storage","ownershipSecret","_params","withErrorHandler","body","parseJsonBody","validation","validateCreateConsent","b","profileId","profile","tenantId","allowedOrigins","checkOriginAllowed","randomVisitorId","ip","ua","input","castCreateConsent","record","setCookie","buildOwnershipSetCookie","params","ownershipError","validateUpdateConsent","castUpdateConsent","result","existing","headers","buildExpireOwnershipCookie","NOTICE_SHOWN_ENABLED","buildNoticeRoutes","notices","request","_params","withErrorHandler","errorResponse","body","parseJsonBody","b","visitorId","profileId","locale","record","randomBytes","authenticate","req","storage","config","secret","auth","token","payload","verifyJwt","sub","email","roles","permissions","allowedTenants","user","permSets","r","p","authError","permission","errorResponse","randomBytes","createHash","base64url","buf","base64urlDecode","s","generatePkce","verifier","challenge","discoverOidcEndpoints","issuer","res","buildAuthorizationUrl","config","authorizationEndpoint","state","nonce","codeChallenge","params","exchangeOidcCode","tokenEndpoint","code","codeVerifier","body","err","verifyOidcIdToken","idToken","jwksUri","parts","header","alg","jwks","jwk","k","cryptoKey","hashName","namedCurve","sigInput","padding","sigB64","sigBytes","c","verifyAlg","payload","now","extractEmailFromClaims","claims","mapping","key","value","extractRolesFromClaims","v","pendingStates","val","storePkceState","consumePkceState","entry","escXml","s","buildSamlMetadataXml","config","validateSamlResponse","samlResponse","samlify","sp","idp","result","email","rolesAttr","roles","r","err","createHmac","randomBytes","timingSafeEqual","BASE32_CHARS","base32Encode","buf","result","bits","value","byte","base32Decode","str","clean","bytes","char","idx","hotp","secret","counter","c","i","hmac","offset","code","generateTotpSecret","usedTokens","now","token","expiresAt","verifyTotp","secret","window","counter","secretBuf","base32Decode","tokenBuf","i","expected","hotp","timingSafeEqual","totpQrUrl","email","issuer","label","params","base64url","buf","buildAdminAuthRoutes","localAuth","storage","authConfig","secret","authLimiter","checkAuthLimit","req","ip","errorResponse","_p","withErrorHandler","limited","body","parseJsonBody","b","token","json","user","authenticate","dbUser","roles","r","_req","endpoints","discoverOidcEndpoints","state","randomBytes","nonce","verifier","challenge","generatePkce","storePkceState","url","buildAuthorizationUrl","code","pkce","consumePkceState","tokens","exchangeOidcCode","claims","verifyOidcIdToken","email","extractEmailFromClaims","extractRolesFromClaims","allRoles","roleName","role","userRoles","permissions","perms","p","xml","buildSamlMetadataXml","samlResponse","info","validateSamlResponse","totpSecret","generateTotpSecret","qrUrl","totpQrUrl","verifyTotp","validateCreateProfile","body","b","pj","validateUpdateProfile","castCreateProfile","tenantId","castUpdateProfile","result","validateProfileCompliance","cookies","categories","complianceGroup","rules","COMPLIANCE_VALIDATION_RULES","errors","warnings","categoryIndex","buildCookieCategoryIndex","categoryId","category","c","rule","cookieId","cookie","legalBasis","getCookieLegalBasis","validateBanner","locale","section","banner","errors","hasVisibleText","buttonId","btn","validateModal","modal","categoryId","category","validateProfileContent","localeContents","content","collectLocaleContents","profileJson","localeContent","result","buildAdminProfileRoutes","service","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","tenantAllowed","tenantId","_p","withErrorHandler","denied","json","p","profile","errorResponse","body","parseJsonBody","validateProfileCompliance","groups","groupId","groupDef","COMPLIANCE_GROUPS","validation","validateCreateProfile","input","castCreateProfile","contentValidation","validateProfileContent","complianceGroup","customComplianceGroup","choice","cookies","categories","complianceValidation","existing","id","validateUpdateProfile","castUpdateProfile","localeContents","name","copy","pj","groupKey","activated","locale","entryId","content","buildAdminConsentRoutes","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","json","url","profileId","getQueryParam","from","to","q","pagination","getPagination","errorResponse","page","limit","filters","records","p","record","history","NOTICE_SHOWN_ENABLED","buildAdminVisitorRoutes","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","json","url","from","getQueryParam","to","q","pagination","getPagination","errorResponse","page","limit","filters","visitors","p","records","buildAdminUserRoutes","userService","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","users","json","p","errorResponse","roles","r","actor","body","parseJsonBody","b","created","id","updated","tenantId","revokedTenantId","buildAdminRoleRoutes","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","roles","json","p","perms","actor","body","parseJsonBody","errorResponse","b","role","buildAdminAuditRoutes","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","json","url","action","getQueryParam","resourceType","from","to","q","pagination","getPagination","errorResponse","page","limit","filters","logs","p","log","buildAdminStatsRoutes","storage","stats","authConfig","secret","auth","req","user","authenticate","authError","_p","withErrorHandler","denied","overview","json","categories","url","days","getQueryInt","timeline","countries","gpc","tenants","rows","t","s","aggregate","acc","r","escapeCsv","val","s","consentToCsvRow","r","auditToCsvRow","buildAdminExportRoutes","storage","authConfig","secret","profileService","auth","req","user","authenticate","authError","_p","withErrorHandler","denied","url","format","getQueryParam","profileId","from","to","filters","records","enc","header","stream","controller","XlsxMod","rows","wb","ws","buf","p","profile","errorResponse","pj","locales","translations","locale","raw","parsed","t","section","content","field","value","logs","randomBytes","createHash","generateApiKey","raw","randomBytes","hash","createHash","buildAdminApiKeyRoutes","storage","authConfig","secret","auth","req","user","authenticate","authError","_p","withErrorHandler","denied","keys","now","result","k","expired","json","body","created","p","buildAdminTenantRoutes","storage","authConfig","secret","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","tenants","json","body","parseJsonBody","errorResponse","b","tenant","p","id","buildAdminSettingsRoutes","storage","authConfig","secret","auth","req","user","authenticate","authError","_p","withErrorHandler","denied","settings","json","body","parseJsonBody","errorResponse","b","buildAdminTcfRoutes","storage","authConfig","secret","auth","req","user","authenticate","authError","_p","withErrorHandler","denied","gvl","getGvl","json","url","search","page","limit","vendors","filtered","v","total","totalPages","safePage","start","LEGAL_BASES","CPRA_CATEGORIES","validateTemplateCookies","cookies","id","cookie","c","COOKIE_PURPOSE_IDS","validateTemplateCategories","categories","entries","category","cat","validateExactlyOneCategoryPerCookie","membershipCount","cookieId","count","validatePurposeLegalBasisAlignment","categoryIdByCookie","categoryId","warnings","legalBasis","purpose","validateConsentTemplate","cookiesResult","categoriesResult","cookiesMap","categoriesMap","membershipResult","buildAdminConsentTemplateRoutes","storage","authConfig","secret","tenantId","auth","req","perm","user","authenticate","authError","withErrorHandler","denied","json","params","t","errorResponse","body","parseJsonBody","templateResult","validateConsentTemplate","id","oldTemplate","update","cookiesChanged","categoriesChanged","nextCookies","nextCategories","profiles","blockingProfiles","p","validateProfileCompliance","src","newName","usage","buildAdminUITemplateRoutes","storage","authConfig","secret","tenantId","auth","req","perm","user","authenticate","authError","withErrorHandler","denied","json","params","t","errorResponse","body","parseJsonBody","update","id","profiles","p","src","newName","usage","buildAdminAnalyticsRoutes","storage","authConfig","secret","defaultTenantId","auth","req","perm","user","authenticate","authError","_p","withErrorHandler","denied","url","tenantId","filters","profileId","complianceGroup","from","to","locale","stats","json","REDACTED","sanitizeConfigForDisplay","config","storage","auth","compliance","dashboard","buildAdminSetupRoutes","profileService","storage","authConfig","secret","config","readiness","auth","req","user","authenticate","authError","rejectIfCompleted","errorResponse","_p","withErrorHandler","denied","settings","json","sanitizeConfigForDisplay","groups","COMPLIANCE_GROUP_IDS","id","COMPLIANCE_GROUPS","completed","parseJsonBody","unknown","createHash","resolveTenant","req","storage","defaultTenantId","auth","rawKey","keyHash","apiKey","RateLimiter","windowMs","maxRequests","now","key","entry","ip","profileId","ConsentAction","options","record","previous","current","status","prevStatus","params","isCategoryGranted","consent","cookieIds","id","CategoryAction","options","record","previous","current","status","prevStatus","params","DIST_DIR","dirname","fileURLToPath","dashboardDir","MIME","tryServeDashboard","pathname","basePath","res","configScript","rel","target","extname","join","raw","readFile","ext","isHtml","ct","injectConfigScript","serveDistFile","filePath","contentType","data","compilePath","path","paramNames","regexParts","part","branded404","html","buildFetchRouter","groups","rateLimiter","storage","resolveTenantId","entries","group","key","handler","spaceIdx","method","pattern","request","corsResponse","handleCors","e","ip","withCors","errorResponse","entry","match","tenantId","adminAllowedOrigins","checkOriginAllowed","params","name","i","val","response","addSecurityHeaders","isUiPath","isPlainObj","v","deepMerge","base","override","result","ov","bv","DEFAULT_CONFIG","createStorageAdapter","config","MongoDBAdapter","MySQLAdapter","PostgreSQLAdapter","NodeSqliteBuiltinAdapter","BetterSqlite3Adapter","NodeSqlite3WasmAdapter","JsonFileAdapter","createConsenti","userConfig","usingJsonStorage","usingDefaultCredentials","storageConfig","authConfig","profilesDir","resolveStoragePaths","eventBus","EventEmitter","profileRepo","ProfileRepo","consentRepo","ConsentRepo","visitorRepo","VisitorRepo","noticeRepo","NoticeRepo","auditRepo","AuditRepo","userRepo","UserRepo","statsService","StatsService","pluginEngine","PluginEngine","compliance","complianceType","needsGeo","localeCache","LocaleJsonCacheService","geoResolver","GeoResolverService","EMBEDDED_COMPLIANCE_MAP","profileService","ProfileService","visitorService","VisitorService","noticeService","NoticeService","consentService","ConsentService","userService","UserService","adminSecret","localAuth","LocalAuth","rl","RateLimiter","authLimiter","req","resolveTenant","_req","apiBase","adminBase","docsPath","dashboardEnabled","buildConsentiConfigScript","buildConsentiRuntimeConfig","swaggerHtml","robotsTxt","fetchRouter","OPENAPI_PUBLIC_SPEC","OPENAPI_ADMIN_SPEC","buildProfileRoutes","buildConsentRoutes","buildNoticeRoutes","buildAdminAuthRoutes","buildAdminProfileRoutes","buildAdminConsentRoutes","buildAdminVisitorRoutes","buildAdminUserRoutes","buildAdminRoleRoutes","buildAdminAuditRoutes","buildAdminStatsRoutes","buildAdminExportRoutes","buildAdminApiKeyRoutes","buildAdminTenantRoutes","buildAdminSettingsRoutes","buildAdminTcfRoutes","buildAdminConsentTemplateRoutes","buildAdminUITemplateRoutes","buildAdminAnalyticsRoutes","buildAdminSetupRoutes","retentionTimer","auditRetentionTimer","resolveReady","rejectReady","ready","rej","startGvlRefresh","days","purge","n","err","purgeAudit","nodeHandler","proto","host","url","nodeRequestToFetch","fetchResponseToNode","PayloadTooLargeError","router","next","fastifyHandler","fastify","_opts","reply","body","stopGvlRefresh"]}