emilsoftware-utilities 2.0.0-dev.80 → 2.0.0-dev.82

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.
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AccessiTenantConfigurationError = void 0;
4
- exports.parseAccessiTenantEnvFile = parseAccessiTenantEnvFile;
4
+ exports.defineAccessiTenantEnvironment = defineAccessiTenantEnvironment;
5
5
  exports.getAccessiTenantRedisPrefix = getAccessiTenantRedisPrefix;
6
+ exports.accessiOptionsFromTenantEnvironment = accessiOptionsFromTenantEnvironment;
7
+ exports.loadAccessiTenantEnvironmentFile = loadAccessiTenantEnvironmentFile;
6
8
  exports.loadAccessiTenants = loadAccessiTenants;
7
9
  const fs_1 = require("fs");
8
10
  const path_1 = require("path");
9
- const accessiOptionsFromEnv_1 = require("../../accessi-module/database-updates/accessiOptionsFromEnv");
11
+ const node_firebird_1 = require("node-firebird");
12
+ const validateOptions_1 = require("../../accessi-module/security/validateOptions");
10
13
  /** Errore aggregato di configurazione tenant. */
11
14
  class AccessiTenantConfigurationError extends Error {
12
15
  constructor(issues) {
@@ -17,54 +20,25 @@ class AccessiTenantConfigurationError extends Error {
17
20
  }
18
21
  exports.AccessiTenantConfigurationError = AccessiTenantConfigurationError;
19
22
  /**
20
- * Parser minimale di un file `.env`.
23
+ * Helper di tipizzazione per i file tenant.
21
24
  *
22
- * Supporta commenti, `export KEY=...`, valori tra apici singoli o doppi e le
23
- * sequenze `\n`, `\r`, `\t`, `\"` e `\\` nei valori tra doppi apici. Non
24
- * interpreta variabili annidate: la configurazione tenant deve restare esplicita.
25
+ * ```ts
26
+ * import type { AccessiTenantEnvironment } from 'emilsoftware-utilities/accessi/server';
27
+ * import { defineAccessiTenantEnvironment } from 'emilsoftware-utilities/accessi/server';
28
+ *
29
+ * export default defineAccessiTenantEnvironment({
30
+ * tenant: { id: 'acme', hosts: ['accessi.acme.example.com'] },
31
+ * database: { host: 'db-acme', database: '/data/acme.fdb' },
32
+ * jwt: { secret: process.env.ACME_JWT_SECRET!, expiresIn: '1h' },
33
+ * email: { ... },
34
+ * encryptionKey: process.env.ACME_ENCRYPTION_KEY!,
35
+ * confirmationEmailUrl: 'https://accessi.acme.example.com',
36
+ * confirmationEmailReturnUrl: 'https://accessi.acme.example.com',
37
+ * });
38
+ * ```
25
39
  */
26
- function parseAccessiTenantEnvFile(content) {
27
- var _a, _b;
28
- const env = {};
29
- for (const rawLine of content.split(/\r?\n/)) {
30
- const line = rawLine.trim();
31
- if (line === '' || line.startsWith('#'))
32
- continue;
33
- const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
34
- if (!match)
35
- throw new Error(`Riga .env non valida: "${rawLine}"`);
36
- const key = (_a = match[1]) !== null && _a !== void 0 ? _a : '';
37
- let value = (_b = match[2]) !== null && _b !== void 0 ? _b : '';
38
- if (value.startsWith('"')) {
39
- if (!value.endsWith('"') || value.length < 2)
40
- throw new Error(`${key}: virgolette non chiuse.`);
41
- value = value.slice(1, -1)
42
- .replace(/\\n/g, '\n')
43
- .replace(/\\r/g, '\r')
44
- .replace(/\\t/g, '\t')
45
- .replace(/\\"/g, '"')
46
- .replace(/\\\\/g, '\\');
47
- }
48
- else if (value.startsWith("'")) {
49
- if (!value.endsWith("'") || value.length < 2)
50
- throw new Error(`${key}: virgolette non chiuse.`);
51
- value = value.slice(1, -1);
52
- }
53
- else {
54
- const comment = value.indexOf(' #');
55
- if (comment >= 0)
56
- value = value.slice(0, comment);
57
- value = value.trim();
58
- }
59
- env[key] = value;
60
- }
61
- return env;
62
- }
63
- function splitList(value) {
64
- return (value !== null && value !== void 0 ? value : '')
65
- .split(',')
66
- .map((entry) => entry.trim().toLowerCase())
67
- .filter(Boolean);
40
+ function defineAccessiTenantEnvironment(environment) {
41
+ return environment;
68
42
  }
69
43
  function normalizeHost(host) {
70
44
  return host.trim().toLowerCase().replace(/\.$/, '');
@@ -97,37 +71,156 @@ function matchesPath(tenant, path) {
97
71
  function getAccessiTenantRedisPrefix(tenantId) {
98
72
  return `accessi:${tenantId}:`;
99
73
  }
100
- function tenantSummary(tenant) {
101
- var _a;
102
- const database = tenant.options.databaseOptions;
103
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id: tenant.id, hosts: [...tenant.hosts] }, (tenant.path ? { path: tenant.path } : {})), { enabled: tenant.enabled }), (tenant.description ? { description: tenant.description } : {})), (tenant.port ? { port: tenant.port } : {})), { file: tenant.file, database: {
104
- host: database.host,
105
- port: database.port,
106
- database: database.database,
107
- user: database.user,
108
- }, issuer: tenant.options.jwtOptions.issuer }), (tenant.options.jwtOptions.audience
109
- ? { audience: typeof tenant.options.jwtOptions.audience === 'string' ? tenant.options.jwtOptions.audience : tenant.options.jwtOptions.audience.join(',') }
110
- : {})), { jwtAlgorithm: (_a = tenant.options.jwtOptions.algorithm) !== null && _a !== void 0 ? _a : 'HS256', refreshEnabled: Boolean(tenant.options.jwtOptions.refreshExpiresIn) });
74
+ /**
75
+ * Converte la configurazione tipizzata in `AccessiOptions`.
76
+ *
77
+ * Applica i default di Firebird e collega `tenantId`; non crea store o sink,
78
+ * che restano responsabilita del backend ospitante.
79
+ */
80
+ function accessiOptionsFromTenantEnvironment(environment) {
81
+ var _a, _b, _c, _d;
82
+ const database = environment.database;
83
+ const databaseOptions = Object.assign(Object.assign({ host: database.host, port: (_a = database.port) !== null && _a !== void 0 ? _a : 3050, database: database.database, user: (_b = database.user) !== null && _b !== void 0 ? _b : 'SYSDBA', password: (_c = database.password) !== null && _c !== void 0 ? _c : 'masterkey' }, (database.pluginName ? { pluginName: database.pluginName } : {})), (database.wireCrypt === 'disabled'
84
+ ? { wireCrypt: node_firebird_1.WIRE_CRYPT_DISABLE }
85
+ : database.wireCrypt === 'optional'
86
+ ? { wireCrypt: node_firebird_1.WIRE_CRYPT_ENABLE }
87
+ : {}));
88
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ databaseOptions, confirmationEmailUrl: environment.confirmationEmailUrl, confirmationEmailReturnUrl: environment.confirmationEmailReturnUrl }, (environment.confirmationEmailPrefix ? { confirmationEmailPrefix: environment.confirmationEmailPrefix } : {})), (environment.customResetPage ? { customResetPage: environment.customResetPage } : {})), (environment.publicBaseUrl ? { publicBaseUrl: environment.publicBaseUrl } : {})), (environment.consoleApiBaseUrl ? { consoleApiBaseUrl: environment.consoleApiBaseUrl } : {})), { encryptionKey: environment.encryptionKey, mockDemoUser: (_d = environment.mockDemoUser) !== null && _d !== void 0 ? _d : false }), (environment.passwordExpiration !== undefined ? { passwordExpiration: environment.passwordExpiration } : {})), (environment.passwordExpirationDays !== undefined ? { passwordExpirationDays: environment.passwordExpirationDays } : {})), (environment.legacyPasswordMigrationOnStartup !== undefined
89
+ ? { legacyPasswordMigrationOnStartup: environment.legacyPasswordMigrationOnStartup }
90
+ : {})), (environment.autoUpdateDatabase !== undefined ? { autoUpdateDatabase: environment.autoUpdateDatabase } : {})), { jwtOptions: environment.jwt, emailOptions: environment.email }), (environment.http ? { http: environment.http } : {})), (environment.authCookie ? { authCookie: environment.authCookie } : {})), (environment.publicAuthRateLimit ? { publicAuthRateLimit: environment.publicAuthRateLimit } : {})), (environment.publicRegistration ? { publicRegistration: environment.publicRegistration } : {})), (environment.federatedAuthentication ? { federatedAuthentication: environment.federatedAuthentication } : {})), (environment.extensionFieldsOptions ? { extensionFieldsOptions: environment.extensionFieldsOptions } : {})), (environment.serviceTokens ? { serviceTokens: environment.serviceTokens } : {})), (environment.catalogScripts ? { catalogScripts: environment.catalogScripts } : {})), (environment.systemSeeds ? { systemSeeds: environment.systemSeeds } : {})), (environment.adminBootstrap ? { adminBootstrap: environment.adminBootstrap } : {})), (environment.openapi ? { openapi: environment.openapi } : {})), (environment.consoleEntry ? { consoleEntry: environment.consoleEntry } : {})), (environment.edgeRateLimit ? { edgeRateLimit: environment.edgeRateLimit } : {})), (environment.sessionRevocation ? { sessionRevocation: environment.sessionRevocation } : {})), (environment.accessTokenRevocation ? { accessTokenRevocation: environment.accessTokenRevocation } : {})), (environment.refreshTokenRotation ? { refreshTokenRotation: environment.refreshTokenRotation } : {})), (environment.metrics ? { metrics: environment.metrics } : {})), (environment.audit ? { audit: environment.audit } : {})), (environment.bufferLogs !== undefined ? { bufferLogs: environment.bufferLogs } : {})), { tenantId: environment.tenant.id });
91
+ }
92
+ function isRecord(value) {
93
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
94
+ }
95
+ function runtimeIssues(environment, file) {
96
+ const issues = [];
97
+ if (!isRecord(environment)) {
98
+ return [`${file}: il file deve esportare un oggetto (default export o export environment).`];
99
+ }
100
+ const tenant = environment.tenant;
101
+ if (!isRecord(tenant)) {
102
+ issues.push(`${file}: tenant mancante.`);
103
+ }
104
+ else {
105
+ if (typeof tenant.id !== 'string' || !/^[a-z0-9][a-z0-9_-]{0,62}$/.test(tenant.id)) {
106
+ issues.push(`${file}: tenant.id non valido (minuscole, numeri, - e _, max 63).`);
107
+ }
108
+ if (tenant.hosts !== undefined && (!Array.isArray(tenant.hosts) || tenant.hosts.some((host) => typeof host !== 'string'))) {
109
+ issues.push(`${file}: tenant.hosts deve essere un array di stringhe.`);
110
+ }
111
+ if (tenant.path !== undefined && typeof tenant.path !== 'string') {
112
+ issues.push(`${file}: tenant.path deve essere una stringa.`);
113
+ }
114
+ if (tenant.port !== undefined
115
+ && (typeof tenant.port !== 'number' || !Number.isInteger(tenant.port) || tenant.port < 1 || tenant.port > 65535)) {
116
+ issues.push(`${file}: tenant.port non valida.`);
117
+ }
118
+ }
119
+ const database = environment.database;
120
+ if (!isRecord(database)) {
121
+ issues.push(`${file}: database mancante.`);
122
+ }
123
+ else {
124
+ if (typeof database.host !== 'string' || database.host.trim() === '') {
125
+ issues.push(`${file}: database.host obbligatorio.`);
126
+ }
127
+ if (typeof database.database !== 'string' || database.database.trim() === '') {
128
+ issues.push(`${file}: database.database obbligatorio.`);
129
+ }
130
+ if (database.port !== undefined
131
+ && (typeof database.port !== 'number' || !Number.isInteger(database.port) || database.port < 1 || database.port > 65535)) {
132
+ issues.push(`${file}: database.port non valida.`);
133
+ }
134
+ }
135
+ if (!isRecord(environment.jwt))
136
+ issues.push(`${file}: jwt mancante.`);
137
+ else {
138
+ if (typeof environment.jwt.secret !== 'string' || environment.jwt.secret === '') {
139
+ issues.push(`${file}: jwt.secret obbligatorio.`);
140
+ }
141
+ if (typeof environment.jwt.expiresIn !== 'string' || environment.jwt.expiresIn === '') {
142
+ issues.push(`${file}: jwt.expiresIn obbligatorio.`);
143
+ }
144
+ }
145
+ if (!isRecord(environment.email))
146
+ issues.push(`${file}: email mancante.`);
147
+ if (typeof environment.encryptionKey !== 'string' || environment.encryptionKey === '') {
148
+ issues.push(`${file}: encryptionKey obbligatoria.`);
149
+ }
150
+ if (typeof environment.confirmationEmailUrl !== 'string') {
151
+ issues.push(`${file}: confirmationEmailUrl obbligatoria.`);
152
+ }
153
+ if (typeof environment.confirmationEmailReturnUrl !== 'string') {
154
+ issues.push(`${file}: confirmationEmailReturnUrl obbligatoria.`);
155
+ }
156
+ return issues;
157
+ }
158
+ function loadEnvironmentModule(file) {
159
+ if (/\.(ts|mts|cts)$/i.test(file)) {
160
+ // Caricamento lazy: jiti serve solo ai file tenant TypeScript.
161
+ const { createJiti } = require('jiti');
162
+ const jiti = createJiti(file, { interopDefault: true, moduleCache: false, fsCache: false });
163
+ return jiti(file);
164
+ }
165
+ return require(file);
166
+ }
167
+ /**
168
+ * Carica un singolo file `*.environment.ts`.
169
+ *
170
+ * Usa jiti per eseguire TypeScript anche nel pacchetto compilato, senza
171
+ * richiedere un passo di build separato per i file tenant.
172
+ */
173
+ function loadAccessiTenantEnvironmentFile(file) {
174
+ var _a, _b, _c;
175
+ const absolute = (0, path_1.resolve)(file);
176
+ let loaded;
177
+ try {
178
+ loaded = loadEnvironmentModule(absolute);
179
+ }
180
+ catch (error) {
181
+ throw new AccessiTenantConfigurationError([
182
+ `${(0, path_1.basename)(file)}: impossibile caricare il file (${error instanceof Error ? error.message : String(error)}).`,
183
+ ]);
184
+ }
185
+ const record = isRecord(loaded) ? loaded : {};
186
+ const environment = ((_b = (_a = record.default) !== null && _a !== void 0 ? _a : record.environment) !== null && _b !== void 0 ? _b : loaded);
187
+ const issues = runtimeIssues(environment, (0, path_1.basename)(file));
188
+ if (issues.length > 0)
189
+ throw new AccessiTenantConfigurationError(issues);
190
+ const typed = environment;
191
+ const settings = typed.tenant;
192
+ const options = accessiOptionsFromTenantEnvironment(typed);
193
+ const validation = (0, validateOptions_1.validateAccessiOptions)(options);
194
+ const validationIssues = validation.errors.map((issue) => `${(0, path_1.basename)(file)}: ${issue.code}: ${issue.message}`);
195
+ if (validationIssues.length > 0)
196
+ throw new AccessiTenantConfigurationError(validationIssues);
197
+ const hosts = ((_c = settings.hosts) !== null && _c !== void 0 ? _c : []).map(normalizeHost).filter(Boolean);
198
+ const path = normalizeTenantPath(settings.path);
199
+ if (hosts.length === 0 && !path) {
200
+ throw new AccessiTenantConfigurationError([
201
+ `${(0, path_1.basename)(file)}: configurare tenant.hosts oppure tenant.path.`,
202
+ ]);
203
+ }
204
+ const tenant = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id: settings.id, hosts }, (path ? { path } : {})), { enabled: settings.enabled !== false }), (settings.description ? { description: settings.description } : {})), (settings.port ? { port: settings.port } : {})), { file: (0, path_1.basename)(file), environmentFile: absolute, options });
205
+ return { tenant, environment: typed };
111
206
  }
112
207
  /**
113
- * Carica i tenant da una cartella di file `<id>.env`.
208
+ * Carica i tenant da una cartella di file `<id>.environment.ts`.
114
209
  *
115
- * Non modifica `process.env`: ogni tenant riceve una copia isolata delle
116
- * variabili. Un errore in un file blocca l'intero caricamento, cosi non si
117
- * avvia un tenant con configurazione parziale.
210
+ * La validazione e aggregata: un errore blocca l'intera cartella, cosi non si
211
+ * avvia un tenant con configurazione parziale. Nessun segreto viene stampato.
118
212
  */
119
213
  function loadAccessiTenants(options) {
120
- var _a, _b, _c, _d, _e, _f, _g;
214
+ var _a, _b, _c, _d, _e;
121
215
  if (!(options === null || options === void 0 ? void 0 : options.directory))
122
216
  throw new Error('loadAccessiTenants: directory obbligatoria.');
123
217
  const directory = (0, path_1.resolve)(options.directory);
124
- const baseEnv = (_a = options.env) !== null && _a !== void 0 ? _a : process.env;
125
218
  const issues = [];
126
219
  const warnings = [];
127
220
  let files;
128
221
  try {
129
222
  files = (0, fs_1.readdirSync)(directory, { withFileTypes: true })
130
- .filter((entry) => entry.isFile() && entry.name.endsWith('.env'))
223
+ .filter((entry) => entry.isFile() && /\.environment\.(ts|mts|cts|js|mjs|cjs)$/.test(entry.name))
131
224
  .map((entry) => entry.name)
132
225
  .sort();
133
226
  }
@@ -137,44 +230,18 @@ function loadAccessiTenants(options) {
137
230
  ]);
138
231
  }
139
232
  if (files.length === 0) {
140
- throw new AccessiTenantConfigurationError([`Nessun file .env nella cartella ${directory}.`]);
233
+ throw new AccessiTenantConfigurationError([`Nessun file *.environment.ts nella cartella ${directory}.`]);
141
234
  }
142
235
  const tenants = [];
143
236
  for (const file of files) {
144
- let fileEnv;
145
- try {
146
- fileEnv = parseAccessiTenantEnvFile((0, fs_1.readFileSync)((0, path_1.join)(directory, file), 'utf8'));
147
- }
148
- catch (error) {
149
- issues.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
150
- continue;
151
- }
152
- const env = Object.assign(Object.assign({}, baseEnv), fileEnv);
153
- const id = ((_b = env.ACCESSI_TENANT_ID) !== null && _b !== void 0 ? _b : (0, path_1.basename)(file, '.env')).trim();
154
- if (!/^[a-z0-9][a-z0-9_-]{0,62}$/.test(id)) {
155
- issues.push(`${file}: ACCESSI_TENANT_ID "${id}" non valido (minuscole, numeri, - e _, max 63).`);
156
- continue;
157
- }
158
- const hosts = splitList(env.ACCESSI_TENANT_HOSTS).map(normalizeHost);
159
- const path = normalizeTenantPath(env.ACCESSI_TENANT_PATH);
160
- if (hosts.length === 0 && !path) {
161
- issues.push(`${file}: configurare ACCESSI_TENANT_HOSTS oppure ACCESSI_TENANT_PATH.`);
162
- continue;
163
- }
164
- const portValue = env.ACCESSI_TENANT_PORT;
165
- const port = portValue ? Number(portValue) : undefined;
166
- if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {
167
- issues.push(`${file}: ACCESSI_TENANT_PORT non valida ("${portValue}").`);
168
- continue;
169
- }
170
237
  try {
171
- const accessiOptions = (0, accessiOptionsFromEnv_1.createAccessiOptionsFromEnv)(env);
172
- tenants.push(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id,
173
- hosts }, (path ? { path } : {})), { enabled: env.ACCESSI_TENANT_ENABLED !== 'false' }), (env.ACCESSI_TENANT_DESCRIPTION ? { description: env.ACCESSI_TENANT_DESCRIPTION } : {})), (port ? { port } : {})), { file,
174
- env, options: Object.assign(Object.assign({}, accessiOptions), { tenantId: id }) }));
238
+ tenants.push(loadAccessiTenantEnvironmentFile((0, path_1.join)(directory, file)).tenant);
175
239
  }
176
240
  catch (error) {
177
- issues.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
241
+ if (error instanceof AccessiTenantConfigurationError)
242
+ issues.push(...error.issues);
243
+ else
244
+ issues.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
178
245
  }
179
246
  }
180
247
  const byId = new Map();
@@ -184,7 +251,7 @@ function loadAccessiTenants(options) {
184
251
  const issuerAudience = new Map();
185
252
  for (const tenant of tenants) {
186
253
  if (byId.has(tenant.id)) {
187
- issues.push(`Tenant duplicato "${tenant.id}" (${tenant.file} e ${(_c = byId.get(tenant.id)) === null || _c === void 0 ? void 0 : _c.file}).`);
254
+ issues.push(`Tenant duplicato "${tenant.id}" (${tenant.file} e ${(_a = byId.get(tenant.id)) === null || _a === void 0 ? void 0 : _a.file}).`);
188
255
  }
189
256
  else {
190
257
  byId.set(tenant.id, tenant);
@@ -204,8 +271,8 @@ function loadAccessiTenants(options) {
204
271
  pathsSeen.set(tenant.path, tenant.id);
205
272
  }
206
273
  // Un segreto JWT condiviso rende un token valido su piu tenant.
207
- const signingKey = (_d = tenant.options.jwtOptions.privateKey) !== null && _d !== void 0 ? _d : tenant.options.jwtOptions.secret;
208
- const fingerprintKey = `${(_e = tenant.options.jwtOptions.algorithm) !== null && _e !== void 0 ? _e : 'HS256'}|${signingKey}`;
274
+ const signingKey = (_b = tenant.options.jwtOptions.privateKey) !== null && _b !== void 0 ? _b : tenant.options.jwtOptions.secret;
275
+ const fingerprintKey = `${(_c = tenant.options.jwtOptions.algorithm) !== null && _c !== void 0 ? _c : 'HS256'}|${signingKey}`;
209
276
  const existingKey = jwtSecrets.get(fingerprintKey);
210
277
  if (existingKey) {
211
278
  issues.push(`Chiave JWT condivisa tra "${existingKey}" e "${tenant.id}": usare segreti distinti.`);
@@ -213,10 +280,10 @@ function loadAccessiTenants(options) {
213
280
  else {
214
281
  jwtSecrets.set(fingerprintKey, tenant.id);
215
282
  }
216
- const issuer = (_f = tenant.options.jwtOptions.issuer) !== null && _f !== void 0 ? _f : '(assente)';
283
+ const issuer = (_d = tenant.options.jwtOptions.issuer) !== null && _d !== void 0 ? _d : '(assente)';
217
284
  const audience = Array.isArray(tenant.options.jwtOptions.audience)
218
285
  ? tenant.options.jwtOptions.audience.join(',')
219
- : ((_g = tenant.options.jwtOptions.audience) !== null && _g !== void 0 ? _g : '(assente)');
286
+ : ((_e = tenant.options.jwtOptions.audience) !== null && _e !== void 0 ? _e : '(assente)');
220
287
  const issuerKey = `${issuer}|${audience}`;
221
288
  const existingIssuer = issuerAudience.get(issuerKey);
222
289
  if (existingIssuer && options.allowSharedIssuer !== true) {
@@ -267,7 +334,22 @@ function loadAccessiTenants(options) {
267
334
  return undefined;
268
335
  },
269
336
  summary() {
270
- return tenants.map(tenantSummary);
337
+ return tenants.map((tenant) => {
338
+ var _a;
339
+ const database = tenant.options.databaseOptions;
340
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id: tenant.id, hosts: [...tenant.hosts] }, (tenant.path ? { path: tenant.path } : {})), { enabled: tenant.enabled }), (tenant.description ? { description: tenant.description } : {})), (tenant.port ? { port: tenant.port } : {})), { file: tenant.file, database: {
341
+ host: database.host,
342
+ port: database.port,
343
+ database: database.database,
344
+ user: database.user,
345
+ }, issuer: tenant.options.jwtOptions.issuer }), (tenant.options.jwtOptions.audience
346
+ ? {
347
+ audience: typeof tenant.options.jwtOptions.audience === 'string'
348
+ ? tenant.options.jwtOptions.audience
349
+ : tenant.options.jwtOptions.audience.join(','),
350
+ }
351
+ : {})), { jwtAlgorithm: (_a = tenant.options.jwtOptions.algorithm) !== null && _a !== void 0 ? _a : 'HS256', refreshEnabled: Boolean(tenant.options.jwtOptions.refreshExpiresIn) });
352
+ });
271
353
  },
272
354
  };
273
355
  return registry;
@@ -1 +1 @@
1
- {"version":3,"file":"tenants.js","sourceRoot":"","sources":["../../../src/accessi/server/tenants.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, readFileSync } from 'fs';\nimport { basename, join, resolve } from 'path';\nimport type { AccessiOptions } from '../../accessi-module/AccessiModule';\nimport { createAccessiOptionsFromEnv } from '../../accessi-module/database-updates/accessiOptionsFromEnv';\n\n/** Variabili usate dal manifest del tenant (oltre alle normali `ACCESSI_*`). */\nexport interface AccessiTenantEnv extends NodeJS.ProcessEnv {\n ACCESSI_TENANT_ID?: string;\n /** Host separati da virgola; supporta `*.example.com`. */\n ACCESSI_TENANT_HOSTS?: string;\n /** Prefisso path, es. `/tenant-a`. */\n ACCESSI_TENANT_PATH?: string;\n ACCESSI_TENANT_ENABLED?: string;\n /** Porta del servizio quando si usa `startAccessiTenants`. */\n ACCESSI_TENANT_PORT?: string;\n ACCESSI_TENANT_DESCRIPTION?: string;\n}\n\nexport interface AccessiTenant {\n id: string;\n hosts: string[];\n path?: string;\n enabled: boolean;\n description?: string;\n port?: number;\n /** Nome del file `.env` di origine (mai il contenuto). */\n file: string;\n /** Variabili risolte del tenant (base + file); non serializzarle nei log. */\n env: NodeJS.ProcessEnv;\n options: AccessiOptions;\n}\n\nexport interface AccessiTenantSummary {\n id: string;\n hosts: string[];\n path?: string;\n enabled: boolean;\n description?: string;\n port?: number;\n file: string;\n database: {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n issuer?: string;\n audience?: string;\n jwtAlgorithm: string;\n refreshEnabled: boolean;\n}\n\nexport interface AccessiTenantResolution {\n tenant: AccessiTenant;\n matchedBy: 'host' | 'path' | 'header';\n}\n\nexport interface AccessiTenantRegistry {\n directory: string;\n tenants: AccessiTenant[];\n warnings: string[];\n allowHeader: boolean;\n byId: ReadonlyMap<string, AccessiTenant>;\n resolve(input: { host?: string; path?: string; header?: string }, options?: { allowHeader?: boolean }): AccessiTenantResolution | undefined;\n summary(): AccessiTenantSummary[];\n}\n\nexport interface LoadAccessiTenantsOptions {\n /** Cartella con `<tenant>.env`. */\n directory: string;\n /** Variabili base (default `process.env`); il file tenant vince. */\n env?: NodeJS.ProcessEnv;\n /** Abilita la risoluzione via header `x-accessi-tenant`. Default: false. */\n allowHeader?: boolean;\n /** Consente issuer/audience uguali tra tenant (sconsigliato). Default: false. */\n allowSharedIssuer?: boolean;\n}\n\n/** Errore aggregato di configurazione tenant. */\nexport class AccessiTenantConfigurationError extends Error {\n readonly issues: string[];\n\n constructor(issues: string[]) {\n super(`Configurazione tenant non valida:\\n${issues.map((issue) => `- ${issue}`).join('\\n')}`);\n this.name = 'AccessiTenantConfigurationError';\n this.issues = issues;\n }\n}\n\n/**\n * Parser minimale di un file `.env`.\n *\n * Supporta commenti, `export KEY=...`, valori tra apici singoli o doppi e le\n * sequenze `\\n`, `\\r`, `\\t`, `\\\"` e `\\\\` nei valori tra doppi apici. Non\n * interpreta variabili annidate: la configurazione tenant deve restare esplicita.\n */\nexport function parseAccessiTenantEnvFile(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n for (const rawLine of content.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (line === '' || line.startsWith('#')) continue;\n const match = /^(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(.*)$/.exec(line);\n if (!match) throw new Error(`Riga .env non valida: \"${rawLine}\"`);\n const key = match[1] ?? '';\n let value = match[2] ?? '';\n if (value.startsWith('\"')) {\n if (!value.endsWith('\"') || value.length < 2) throw new Error(`${key}: virgolette non chiuse.`);\n value = value.slice(1, -1)\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\\\\\/g, '\\\\');\n } else if (value.startsWith(\"'\")) {\n if (!value.endsWith(\"'\") || value.length < 2) throw new Error(`${key}: virgolette non chiuse.`);\n value = value.slice(1, -1);\n } else {\n const comment = value.indexOf(' #');\n if (comment >= 0) value = value.slice(0, comment);\n value = value.trim();\n }\n env[key] = value;\n }\n return env;\n}\n\nfunction splitList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim().toLowerCase())\n .filter(Boolean);\n}\n\nfunction normalizeHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction normalizeTenantPath(path: string | undefined): string | undefined {\n if (path === undefined || path.trim() === '') return undefined;\n const normalized = `/${path.trim().replace(/^\\/+|\\/+$/g, '')}`;\n return normalized === '/' ? '/' : normalized;\n}\n\nfunction matchesHost(tenant: AccessiTenant, host: string): boolean {\n return tenant.hosts.some((candidate) => {\n if (candidate === host) return true;\n if (candidate.startsWith('*.')) {\n const suffix = candidate.slice(1);\n return host.endsWith(suffix) && host.length > suffix.length;\n }\n return false;\n });\n}\n\nfunction matchesPath(tenant: AccessiTenant, path: string): boolean {\n if (!tenant.path) return false;\n if (tenant.path === '/') return true;\n return path === tenant.path || path.startsWith(`${tenant.path}/`);\n}\n\n/** Prefisso Redis consigliato per isolare gli store del tenant. */\nexport function getAccessiTenantRedisPrefix(tenantId: string): string {\n return `accessi:${tenantId}:`;\n}\n\nfunction tenantSummary(tenant: AccessiTenant): AccessiTenantSummary {\n const database = tenant.options.databaseOptions as {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n return {\n id: tenant.id,\n hosts: [...tenant.hosts],\n ...(tenant.path ? { path: tenant.path } : {}),\n enabled: tenant.enabled,\n ...(tenant.description ? { description: tenant.description } : {}),\n ...(tenant.port ? { port: tenant.port } : {}),\n file: tenant.file,\n database: {\n host: database.host,\n port: database.port,\n database: database.database,\n user: database.user,\n },\n issuer: tenant.options.jwtOptions.issuer,\n ...(tenant.options.jwtOptions.audience\n ? { audience: typeof tenant.options.jwtOptions.audience === 'string' ? tenant.options.jwtOptions.audience : tenant.options.jwtOptions.audience.join(',') }\n : {}),\n jwtAlgorithm: tenant.options.jwtOptions.algorithm ?? 'HS256',\n refreshEnabled: Boolean(tenant.options.jwtOptions.refreshExpiresIn),\n };\n}\n\n/**\n * Carica i tenant da una cartella di file `<id>.env`.\n *\n * Non modifica `process.env`: ogni tenant riceve una copia isolata delle\n * variabili. Un errore in un file blocca l'intero caricamento, cosi non si\n * avvia un tenant con configurazione parziale.\n */\nexport function loadAccessiTenants(options: LoadAccessiTenantsOptions): AccessiTenantRegistry {\n if (!options?.directory) throw new Error('loadAccessiTenants: directory obbligatoria.');\n const directory = resolve(options.directory);\n const baseEnv = options.env ?? process.env;\n const issues: string[] = [];\n const warnings: string[] = [];\n\n let files: string[];\n try {\n files = readdirSync(directory, { withFileTypes: true })\n .filter((entry) => entry.isFile() && entry.name.endsWith('.env'))\n .map((entry) => entry.name)\n .sort();\n } catch (error) {\n throw new AccessiTenantConfigurationError([\n `Cartella tenant non leggibile (${directory}): ${error instanceof Error ? error.message : String(error)}`,\n ]);\n }\n if (files.length === 0) {\n throw new AccessiTenantConfigurationError([`Nessun file .env nella cartella ${directory}.`]);\n }\n\n const tenants: AccessiTenant[] = [];\n for (const file of files) {\n let fileEnv: Record<string, string>;\n try {\n fileEnv = parseAccessiTenantEnvFile(readFileSync(join(directory, file), 'utf8'));\n } catch (error) {\n issues.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);\n continue;\n }\n\n const env: NodeJS.ProcessEnv = { ...baseEnv, ...fileEnv };\n const id = (env.ACCESSI_TENANT_ID ?? basename(file, '.env')).trim();\n if (!/^[a-z0-9][a-z0-9_-]{0,62}$/.test(id)) {\n issues.push(`${file}: ACCESSI_TENANT_ID \"${id}\" non valido (minuscole, numeri, - e _, max 63).`);\n continue;\n }\n const hosts = splitList(env.ACCESSI_TENANT_HOSTS).map(normalizeHost);\n const path = normalizeTenantPath(env.ACCESSI_TENANT_PATH);\n if (hosts.length === 0 && !path) {\n issues.push(`${file}: configurare ACCESSI_TENANT_HOSTS oppure ACCESSI_TENANT_PATH.`);\n continue;\n }\n const portValue = env.ACCESSI_TENANT_PORT;\n const port = portValue ? Number(portValue) : undefined;\n if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {\n issues.push(`${file}: ACCESSI_TENANT_PORT non valida (\"${portValue}\").`);\n continue;\n }\n\n try {\n const accessiOptions = createAccessiOptionsFromEnv(env);\n tenants.push({\n id,\n hosts,\n ...(path ? { path } : {}),\n enabled: env.ACCESSI_TENANT_ENABLED !== 'false',\n ...(env.ACCESSI_TENANT_DESCRIPTION ? { description: env.ACCESSI_TENANT_DESCRIPTION } : {}),\n ...(port ? { port } : {}),\n file,\n env,\n options: { ...accessiOptions, tenantId: id },\n });\n } catch (error) {\n issues.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n const byId = new Map<string, AccessiTenant>();\n const hostsSeen = new Map<string, string>();\n const pathsSeen = new Map<string, string>();\n const jwtSecrets = new Map<string, string>();\n const issuerAudience = new Map<string, string>();\n for (const tenant of tenants) {\n if (byId.has(tenant.id)) {\n issues.push(`Tenant duplicato \"${tenant.id}\" (${tenant.file} e ${byId.get(tenant.id)?.file}).`);\n } else {\n byId.set(tenant.id, tenant);\n }\n for (const host of tenant.hosts) {\n const existing = hostsSeen.get(host);\n if (existing) issues.push(`Host \"${host}\" assegnato a \"${existing}\" e \"${tenant.id}\".`);\n else hostsSeen.set(host, tenant.id);\n }\n if (tenant.path) {\n const existing = pathsSeen.get(tenant.path);\n if (existing) issues.push(`Path \"${tenant.path}\" assegnato a \"${existing}\" e \"${tenant.id}\".`);\n else pathsSeen.set(tenant.path, tenant.id);\n }\n\n // Un segreto JWT condiviso rende un token valido su piu tenant.\n const signingKey = tenant.options.jwtOptions.privateKey ?? tenant.options.jwtOptions.secret;\n const fingerprintKey = `${tenant.options.jwtOptions.algorithm ?? 'HS256'}|${signingKey}`;\n const existingKey = jwtSecrets.get(fingerprintKey);\n if (existingKey) {\n issues.push(`Chiave JWT condivisa tra \"${existingKey}\" e \"${tenant.id}\": usare segreti distinti.`);\n } else {\n jwtSecrets.set(fingerprintKey, tenant.id);\n }\n\n const issuer = tenant.options.jwtOptions.issuer ?? '(assente)';\n const audience = Array.isArray(tenant.options.jwtOptions.audience)\n ? tenant.options.jwtOptions.audience.join(',')\n : (tenant.options.jwtOptions.audience ?? '(assente)');\n const issuerKey = `${issuer}|${audience}`;\n const existingIssuer = issuerAudience.get(issuerKey);\n if (existingIssuer && options.allowSharedIssuer !== true) {\n warnings.push(`Issuer/audience \"${issuerKey}\" condivisi tra \"${existingIssuer}\" e \"${tenant.id}\".`);\n } else {\n issuerAudience.set(issuerKey, tenant.id);\n }\n }\n\n const enabled = tenants.filter((tenant) => tenant.enabled);\n if (enabled.length > 1) {\n const defaultTenant = enabled.find((tenant) => tenant.hosts.length === 0 && tenant.path === '/');\n if (defaultTenant) {\n issues.push(`Tenant \"${defaultTenant.id}\" intercetta tutte le richieste (path \"/\") insieme ad altri tenant.`);\n }\n }\n\n if (issues.length > 0) throw new AccessiTenantConfigurationError(issues);\n\n const registry: AccessiTenantRegistry = {\n directory,\n tenants,\n warnings,\n allowHeader: options.allowHeader === true,\n byId,\n resolve(input, resolveOptions) {\n const allowHeader = resolveOptions?.allowHeader ?? registry.allowHeader;\n const host = input.host ? normalizeHost(input.host.split(':')[0] ?? input.host) : undefined;\n if (host) {\n const byHost = enabled.find((tenant) => matchesHost(tenant, host));\n if (byHost) return { tenant: byHost, matchedBy: 'host' };\n }\n\n const requestPath = input.path ?? '/';\n const byPath = enabled\n .filter((tenant) => matchesPath(tenant, requestPath))\n .sort((left, right) => (right.path?.length ?? 0) - (left.path?.length ?? 0))[0];\n if (byPath && byPath.path !== '/') return { tenant: byPath, matchedBy: 'path' };\n\n if (allowHeader && input.header) {\n const headerId = input.header.trim().toLowerCase();\n const tenant = byId.get(headerId);\n if (tenant?.enabled) return { tenant, matchedBy: 'header' };\n }\n\n if (byPath) return { tenant: byPath, matchedBy: 'path' };\n return undefined;\n },\n summary() {\n return tenants.map(tenantSummary);\n },\n };\n\n return registry;\n}\n\n"]}
1
+ {"version":3,"file":"tenants.js","sourceRoot":"","sources":["../../../src/accessi/server/tenants.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync } from 'fs';\nimport { basename, join, resolve } from 'path';\nimport { WIRE_CRYPT_DISABLE, WIRE_CRYPT_ENABLE } from 'node-firebird';\nimport type { Options } from 'node-firebird';\nimport type {\n AccessiOptions,\n AccessiHttpOptions,\n AdminBootstrapOptions,\n CatalogScriptsOptions,\n ConsoleEntryOptions,\n EmailOptions,\n ExtensionFieldsOptions,\n FederatedAuthenticationOptions,\n JwtOptions,\n PublicAuthRateLimitOptions,\n PublicRegistrationOptions,\n ServiceTokenOptions,\n SystemSeedsOptions,\n} from '../../accessi-module/AccessiModule';\nimport type { AccessiEdgeRateLimitOptions } from '../../accessi-module/security/edgeRateLimit';\nimport type { AccessiAuthCookieOptions } from '../../accessi-module/security/authCookies';\nimport type { AccessiMetricsOptions } from '../../accessi-module/observability/metrics';\nimport type { AccessiAuditOptions } from '../../accessi-module/security/audit';\nimport type { SessionRevocationOptions } from '../../accessi-module/security/sessionRevocation';\nimport type { AccessTokenRevocationOptions } from '../../accessi-module/security/accessTokenRevocation';\nimport type { RefreshTokenRotationOptions } from '../../accessi-module/security/refreshTokenRotation';\nimport type { OpenApiSetupOptions } from '../../openapi/OpenApiConfig';\nimport { validateAccessiOptions } from '../../accessi-module/security/validateOptions';\n\n/** Identita e routing del tenant. */\nexport interface AccessiTenantSettings {\n /** Minuscole, numeri, `-` e `_`; max 63 caratteri. */\n id: string;\n /** Host esatti o wildcard `*.example.com`. */\n hosts?: string[];\n /** Prefisso path, es. `/acme`. */\n path?: string;\n /** Porta del processo quando si usa il supervisore. */\n port?: number;\n /** Default `true`. */\n enabled?: boolean;\n description?: string;\n}\n\n/** Connessione Firebird del tenant, tipizzata senza callback. */\nexport interface AccessiTenantDatabaseOptions {\n host: string;\n port?: number;\n database: string;\n user?: string;\n password?: string;\n /** `disabled`/`optional`; `required` e il default del driver. */\n wireCrypt?: 'required' | 'optional' | 'disabled';\n pluginName?: string;\n}\n\n/**\n * Configurazione serializzabile di un tenant.\n *\n * I campi che contengono store, callback o logger (`store`, `sink`, `match`,\n * `onDocument`, `logger`, ...) sono esclusi: vanno collegati dal backend\n * ospitante, non da un file di configurazione.\n */\nexport interface AccessiTenantEnvironment {\n tenant: AccessiTenantSettings;\n database: AccessiTenantDatabaseOptions;\n jwt: JwtOptions;\n email: EmailOptions;\n encryptionKey: string;\n confirmationEmailUrl: string;\n confirmationEmailReturnUrl: string;\n confirmationEmailPrefix?: string;\n customResetPage?: string;\n publicBaseUrl?: string;\n consoleApiBaseUrl?: string;\n mockDemoUser?: boolean;\n passwordExpiration?: boolean;\n passwordExpirationDays?: number;\n legacyPasswordMigrationOnStartup?: boolean;\n autoUpdateDatabase?: boolean;\n http?: AccessiHttpOptions;\n authCookie?: AccessiAuthCookieOptions;\n publicAuthRateLimit?: Omit<PublicAuthRateLimitOptions, 'store'>;\n publicRegistration?: PublicRegistrationOptions;\n federatedAuthentication?: FederatedAuthenticationOptions;\n extensionFieldsOptions?: ExtensionFieldsOptions[];\n serviceTokens?: Omit<ServiceTokenOptions, 'rateLimitStore'>;\n catalogScripts?: CatalogScriptsOptions;\n systemSeeds?: SystemSeedsOptions;\n adminBootstrap?: AdminBootstrapOptions;\n openapi?: Omit<OpenApiSetupOptions, 'onDocument'>;\n consoleEntry?: Omit<ConsoleEntryOptions, 'store'>;\n edgeRateLimit?: Omit<AccessiEdgeRateLimitOptions, 'store' | 'match'>;\n sessionRevocation?: Omit<SessionRevocationOptions, 'store'>;\n accessTokenRevocation?: Omit<AccessTokenRevocationOptions, 'store'>;\n refreshTokenRotation?: Omit<RefreshTokenRotationOptions, 'store'>;\n metrics?: Omit<AccessiMetricsOptions, 'sink'>;\n audit?: Omit<AccessiAuditOptions, 'sinks' | 'store' | 'tenantId'>;\n bufferLogs?: boolean;\n}\n\nexport interface AccessiTenant {\n id: string;\n hosts: string[];\n path?: string;\n enabled: boolean;\n description?: string;\n port?: number;\n /** Nome del file `*.environment.ts` di origine (mai il contenuto). */\n file: string;\n environmentFile: string;\n options: AccessiOptions;\n}\n\nexport interface AccessiTenantSummary {\n id: string;\n hosts: string[];\n path?: string;\n enabled: boolean;\n description?: string;\n port?: number;\n file: string;\n database: {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n issuer?: string;\n audience?: string;\n jwtAlgorithm: string;\n refreshEnabled: boolean;\n}\n\nexport interface AccessiTenantResolution {\n tenant: AccessiTenant;\n matchedBy: 'host' | 'path' | 'header';\n}\n\nexport interface AccessiTenantRegistry {\n directory: string;\n tenants: AccessiTenant[];\n warnings: string[];\n allowHeader: boolean;\n byId: ReadonlyMap<string, AccessiTenant>;\n resolve(input: { host?: string; path?: string; header?: string }, options?: { allowHeader?: boolean }): AccessiTenantResolution | undefined;\n summary(): AccessiTenantSummary[];\n}\n\nexport interface LoadAccessiTenantsOptions {\n /** Cartella con `<id>.environment.ts`. */\n directory: string;\n /** Abilita la risoluzione via header `x-accessi-tenant`. Default: false. */\n allowHeader?: boolean;\n /** Consente issuer/audience uguali tra tenant (sconsigliato). Default: false. */\n allowSharedIssuer?: boolean;\n}\n\n/** Errore aggregato di configurazione tenant. */\nexport class AccessiTenantConfigurationError extends Error {\n readonly issues: string[];\n\n constructor(issues: string[]) {\n super(`Configurazione tenant non valida:\\n${issues.map((issue) => `- ${issue}`).join('\\n')}`);\n this.name = 'AccessiTenantConfigurationError';\n this.issues = issues;\n }\n}\n\n/**\n * Helper di tipizzazione per i file tenant.\n *\n * ```ts\n * import type { AccessiTenantEnvironment } from 'emilsoftware-utilities/accessi/server';\n * import { defineAccessiTenantEnvironment } from 'emilsoftware-utilities/accessi/server';\n *\n * export default defineAccessiTenantEnvironment({\n * tenant: { id: 'acme', hosts: ['accessi.acme.example.com'] },\n * database: { host: 'db-acme', database: '/data/acme.fdb' },\n * jwt: { secret: process.env.ACME_JWT_SECRET!, expiresIn: '1h' },\n * email: { ... },\n * encryptionKey: process.env.ACME_ENCRYPTION_KEY!,\n * confirmationEmailUrl: 'https://accessi.acme.example.com',\n * confirmationEmailReturnUrl: 'https://accessi.acme.example.com',\n * });\n * ```\n */\nexport function defineAccessiTenantEnvironment<const T extends AccessiTenantEnvironment>(\n environment: T,\n): T {\n return environment;\n}\n\nfunction normalizeHost(host: string): string {\n return host.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nfunction normalizeTenantPath(path: string | undefined): string | undefined {\n if (path === undefined || path.trim() === '') return undefined;\n const normalized = `/${path.trim().replace(/^\\/+|\\/+$/g, '')}`;\n return normalized === '/' ? '/' : normalized;\n}\n\nfunction matchesHost(tenant: AccessiTenant, host: string): boolean {\n return tenant.hosts.some((candidate) => {\n if (candidate === host) return true;\n if (candidate.startsWith('*.')) {\n const suffix = candidate.slice(1);\n return host.endsWith(suffix) && host.length > suffix.length;\n }\n return false;\n });\n}\n\nfunction matchesPath(tenant: AccessiTenant, path: string): boolean {\n if (!tenant.path) return false;\n if (tenant.path === '/') return true;\n return path === tenant.path || path.startsWith(`${tenant.path}/`);\n}\n\n/** Prefisso Redis consigliato per isolare gli store del tenant. */\nexport function getAccessiTenantRedisPrefix(tenantId: string): string {\n return `accessi:${tenantId}:`;\n}\n\n/**\n * Converte la configurazione tipizzata in `AccessiOptions`.\n *\n * Applica i default di Firebird e collega `tenantId`; non crea store o sink,\n * che restano responsabilita del backend ospitante.\n */\nexport function accessiOptionsFromTenantEnvironment(\n environment: AccessiTenantEnvironment,\n): AccessiOptions {\n const database = environment.database;\n const databaseOptions: Options = {\n host: database.host,\n port: database.port ?? 3050,\n database: database.database,\n user: database.user ?? 'SYSDBA',\n password: database.password ?? 'masterkey',\n ...(database.pluginName ? { pluginName: database.pluginName } : {}),\n ...(database.wireCrypt === 'disabled'\n ? { wireCrypt: WIRE_CRYPT_DISABLE }\n : database.wireCrypt === 'optional'\n ? { wireCrypt: WIRE_CRYPT_ENABLE }\n : {}),\n };\n\n return {\n databaseOptions,\n confirmationEmailUrl: environment.confirmationEmailUrl,\n confirmationEmailReturnUrl: environment.confirmationEmailReturnUrl,\n ...(environment.confirmationEmailPrefix ? { confirmationEmailPrefix: environment.confirmationEmailPrefix } : {}),\n ...(environment.customResetPage ? { customResetPage: environment.customResetPage } : {}),\n ...(environment.publicBaseUrl ? { publicBaseUrl: environment.publicBaseUrl } : {}),\n ...(environment.consoleApiBaseUrl ? { consoleApiBaseUrl: environment.consoleApiBaseUrl } : {}),\n encryptionKey: environment.encryptionKey,\n mockDemoUser: environment.mockDemoUser ?? false,\n ...(environment.passwordExpiration !== undefined ? { passwordExpiration: environment.passwordExpiration } : {}),\n ...(environment.passwordExpirationDays !== undefined ? { passwordExpirationDays: environment.passwordExpirationDays } : {}),\n ...(environment.legacyPasswordMigrationOnStartup !== undefined\n ? { legacyPasswordMigrationOnStartup: environment.legacyPasswordMigrationOnStartup }\n : {}),\n ...(environment.autoUpdateDatabase !== undefined ? { autoUpdateDatabase: environment.autoUpdateDatabase } : {}),\n jwtOptions: environment.jwt,\n emailOptions: environment.email,\n ...(environment.http ? { http: environment.http } : {}),\n ...(environment.authCookie ? { authCookie: environment.authCookie } : {}),\n ...(environment.publicAuthRateLimit ? { publicAuthRateLimit: environment.publicAuthRateLimit } : {}),\n ...(environment.publicRegistration ? { publicRegistration: environment.publicRegistration } : {}),\n ...(environment.federatedAuthentication ? { federatedAuthentication: environment.federatedAuthentication } : {}),\n ...(environment.extensionFieldsOptions ? { extensionFieldsOptions: environment.extensionFieldsOptions } : {}),\n ...(environment.serviceTokens ? { serviceTokens: environment.serviceTokens } : {}),\n ...(environment.catalogScripts ? { catalogScripts: environment.catalogScripts } : {}),\n ...(environment.systemSeeds ? { systemSeeds: environment.systemSeeds } : {}),\n ...(environment.adminBootstrap ? { adminBootstrap: environment.adminBootstrap } : {}),\n ...(environment.openapi ? { openapi: environment.openapi } : {}),\n ...(environment.consoleEntry ? { consoleEntry: environment.consoleEntry } : {}),\n ...(environment.edgeRateLimit ? { edgeRateLimit: environment.edgeRateLimit } : {}),\n ...(environment.sessionRevocation ? { sessionRevocation: environment.sessionRevocation } : {}),\n ...(environment.accessTokenRevocation ? { accessTokenRevocation: environment.accessTokenRevocation } : {}),\n ...(environment.refreshTokenRotation ? { refreshTokenRotation: environment.refreshTokenRotation } : {}),\n ...(environment.metrics ? { metrics: environment.metrics } : {}),\n ...(environment.audit ? { audit: environment.audit } : {}),\n ...(environment.bufferLogs !== undefined ? { bufferLogs: environment.bufferLogs } : {}),\n tenantId: environment.tenant.id,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction runtimeIssues(environment: unknown, file: string): string[] {\n const issues: string[] = [];\n if (!isRecord(environment)) {\n return [`${file}: il file deve esportare un oggetto (default export o export environment).`];\n }\n const tenant = environment.tenant;\n if (!isRecord(tenant)) {\n issues.push(`${file}: tenant mancante.`);\n } else {\n if (typeof tenant.id !== 'string' || !/^[a-z0-9][a-z0-9_-]{0,62}$/.test(tenant.id)) {\n issues.push(`${file}: tenant.id non valido (minuscole, numeri, - e _, max 63).`);\n }\n if (tenant.hosts !== undefined && (!Array.isArray(tenant.hosts) || tenant.hosts.some((host) => typeof host !== 'string'))) {\n issues.push(`${file}: tenant.hosts deve essere un array di stringhe.`);\n }\n if (tenant.path !== undefined && typeof tenant.path !== 'string') {\n issues.push(`${file}: tenant.path deve essere una stringa.`);\n }\n if (\n tenant.port !== undefined\n && (typeof tenant.port !== 'number' || !Number.isInteger(tenant.port) || tenant.port < 1 || tenant.port > 65535)\n ) {\n issues.push(`${file}: tenant.port non valida.`);\n }\n }\n\n const database = environment.database;\n if (!isRecord(database)) {\n issues.push(`${file}: database mancante.`);\n } else {\n if (typeof database.host !== 'string' || database.host.trim() === '') {\n issues.push(`${file}: database.host obbligatorio.`);\n }\n if (typeof database.database !== 'string' || database.database.trim() === '') {\n issues.push(`${file}: database.database obbligatorio.`);\n }\n if (\n database.port !== undefined\n && (typeof database.port !== 'number' || !Number.isInteger(database.port) || database.port < 1 || database.port > 65535)\n ) {\n issues.push(`${file}: database.port non valida.`);\n }\n }\n\n if (!isRecord(environment.jwt)) issues.push(`${file}: jwt mancante.`);\n else {\n if (typeof environment.jwt.secret !== 'string' || environment.jwt.secret === '') {\n issues.push(`${file}: jwt.secret obbligatorio.`);\n }\n if (typeof environment.jwt.expiresIn !== 'string' || environment.jwt.expiresIn === '') {\n issues.push(`${file}: jwt.expiresIn obbligatorio.`);\n }\n }\n\n if (!isRecord(environment.email)) issues.push(`${file}: email mancante.`);\n if (typeof environment.encryptionKey !== 'string' || environment.encryptionKey === '') {\n issues.push(`${file}: encryptionKey obbligatoria.`);\n }\n if (typeof environment.confirmationEmailUrl !== 'string') {\n issues.push(`${file}: confirmationEmailUrl obbligatoria.`);\n }\n if (typeof environment.confirmationEmailReturnUrl !== 'string') {\n issues.push(`${file}: confirmationEmailReturnUrl obbligatoria.`);\n }\n return issues;\n}\n\nfunction loadEnvironmentModule(file: string): unknown {\n if (/\\.(ts|mts|cts)$/i.test(file)) {\n // Caricamento lazy: jiti serve solo ai file tenant TypeScript.\n const { createJiti } = require('jiti') as {\n createJiti: (\n id: string,\n options?: { interopDefault?: boolean; moduleCache?: boolean; fsCache?: boolean },\n ) => (id: string) => unknown;\n };\n const jiti = createJiti(file, { interopDefault: true, moduleCache: false, fsCache: false });\n return jiti(file);\n }\n return require(file);\n}\n\n/**\n * Carica un singolo file `*.environment.ts`.\n *\n * Usa jiti per eseguire TypeScript anche nel pacchetto compilato, senza\n * richiedere un passo di build separato per i file tenant.\n */\nexport function loadAccessiTenantEnvironmentFile(file: string): { tenant: AccessiTenant; environment: AccessiTenantEnvironment } {\n const absolute = resolve(file);\n let loaded: unknown;\n try {\n loaded = loadEnvironmentModule(absolute);\n } catch (error) {\n throw new AccessiTenantConfigurationError([\n `${basename(file)}: impossibile caricare il file (${error instanceof Error ? error.message : String(error)}).`,\n ]);\n }\n\n const record = isRecord(loaded) ? loaded : {};\n const environment = (record.default ?? record.environment ?? loaded) as unknown;\n const issues = runtimeIssues(environment, basename(file));\n if (issues.length > 0) throw new AccessiTenantConfigurationError(issues);\n\n const typed = environment as AccessiTenantEnvironment;\n const settings = typed.tenant;\n const options = accessiOptionsFromTenantEnvironment(typed);\n const validation = validateAccessiOptions(options);\n const validationIssues = validation.errors.map((issue) => `${basename(file)}: ${issue.code}: ${issue.message}`);\n if (validationIssues.length > 0) throw new AccessiTenantConfigurationError(validationIssues);\n\n const hosts = (settings.hosts ?? []).map(normalizeHost).filter(Boolean);\n const path = normalizeTenantPath(settings.path);\n if (hosts.length === 0 && !path) {\n throw new AccessiTenantConfigurationError([\n `${basename(file)}: configurare tenant.hosts oppure tenant.path.`,\n ]);\n }\n\n const tenant: AccessiTenant = {\n id: settings.id,\n hosts,\n ...(path ? { path } : {}),\n enabled: settings.enabled !== false,\n ...(settings.description ? { description: settings.description } : {}),\n ...(settings.port ? { port: settings.port } : {}),\n file: basename(file),\n environmentFile: absolute,\n options,\n };\n return { tenant, environment: typed };\n}\n\n/**\n * Carica i tenant da una cartella di file `<id>.environment.ts`.\n *\n * La validazione e aggregata: un errore blocca l'intera cartella, cosi non si\n * avvia un tenant con configurazione parziale. Nessun segreto viene stampato.\n */\nexport function loadAccessiTenants(options: LoadAccessiTenantsOptions): AccessiTenantRegistry {\n if (!options?.directory) throw new Error('loadAccessiTenants: directory obbligatoria.');\n const directory = resolve(options.directory);\n const issues: string[] = [];\n const warnings: string[] = [];\n\n let files: string[];\n try {\n files = readdirSync(directory, { withFileTypes: true })\n .filter((entry) => entry.isFile() && /\\.environment\\.(ts|mts|cts|js|mjs|cjs)$/.test(entry.name))\n .map((entry) => entry.name)\n .sort();\n } catch (error) {\n throw new AccessiTenantConfigurationError([\n `Cartella tenant non leggibile (${directory}): ${error instanceof Error ? error.message : String(error)}`,\n ]);\n }\n if (files.length === 0) {\n throw new AccessiTenantConfigurationError([`Nessun file *.environment.ts nella cartella ${directory}.`]);\n }\n\n const tenants: AccessiTenant[] = [];\n for (const file of files) {\n try {\n tenants.push(loadAccessiTenantEnvironmentFile(join(directory, file)).tenant);\n } catch (error) {\n if (error instanceof AccessiTenantConfigurationError) issues.push(...error.issues);\n else issues.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n const byId = new Map<string, AccessiTenant>();\n const hostsSeen = new Map<string, string>();\n const pathsSeen = new Map<string, string>();\n const jwtSecrets = new Map<string, string>();\n const issuerAudience = new Map<string, string>();\n for (const tenant of tenants) {\n if (byId.has(tenant.id)) {\n issues.push(`Tenant duplicato \"${tenant.id}\" (${tenant.file} e ${byId.get(tenant.id)?.file}).`);\n } else {\n byId.set(tenant.id, tenant);\n }\n for (const host of tenant.hosts) {\n const existing = hostsSeen.get(host);\n if (existing) issues.push(`Host \"${host}\" assegnato a \"${existing}\" e \"${tenant.id}\".`);\n else hostsSeen.set(host, tenant.id);\n }\n if (tenant.path) {\n const existing = pathsSeen.get(tenant.path);\n if (existing) issues.push(`Path \"${tenant.path}\" assegnato a \"${existing}\" e \"${tenant.id}\".`);\n else pathsSeen.set(tenant.path, tenant.id);\n }\n\n // Un segreto JWT condiviso rende un token valido su piu tenant.\n const signingKey = tenant.options.jwtOptions.privateKey ?? tenant.options.jwtOptions.secret;\n const fingerprintKey = `${tenant.options.jwtOptions.algorithm ?? 'HS256'}|${signingKey}`;\n const existingKey = jwtSecrets.get(fingerprintKey);\n if (existingKey) {\n issues.push(`Chiave JWT condivisa tra \"${existingKey}\" e \"${tenant.id}\": usare segreti distinti.`);\n } else {\n jwtSecrets.set(fingerprintKey, tenant.id);\n }\n\n const issuer = tenant.options.jwtOptions.issuer ?? '(assente)';\n const audience = Array.isArray(tenant.options.jwtOptions.audience)\n ? tenant.options.jwtOptions.audience.join(',')\n : (tenant.options.jwtOptions.audience ?? '(assente)');\n const issuerKey = `${issuer}|${audience}`;\n const existingIssuer = issuerAudience.get(issuerKey);\n if (existingIssuer && options.allowSharedIssuer !== true) {\n warnings.push(`Issuer/audience \"${issuerKey}\" condivisi tra \"${existingIssuer}\" e \"${tenant.id}\".`);\n } else {\n issuerAudience.set(issuerKey, tenant.id);\n }\n }\n\n const enabled = tenants.filter((tenant) => tenant.enabled);\n if (enabled.length > 1) {\n const defaultTenant = enabled.find((tenant) => tenant.hosts.length === 0 && tenant.path === '/');\n if (defaultTenant) {\n issues.push(`Tenant \"${defaultTenant.id}\" intercetta tutte le richieste (path \"/\") insieme ad altri tenant.`);\n }\n }\n\n if (issues.length > 0) throw new AccessiTenantConfigurationError(issues);\n\n const registry: AccessiTenantRegistry = {\n directory,\n tenants,\n warnings,\n allowHeader: options.allowHeader === true,\n byId,\n resolve(input, resolveOptions) {\n const allowHeader = resolveOptions?.allowHeader ?? registry.allowHeader;\n const host = input.host ? normalizeHost(input.host.split(':')[0] ?? input.host) : undefined;\n if (host) {\n const byHost = enabled.find((tenant) => matchesHost(tenant, host));\n if (byHost) return { tenant: byHost, matchedBy: 'host' };\n }\n\n const requestPath = input.path ?? '/';\n const byPath = enabled\n .filter((tenant) => matchesPath(tenant, requestPath))\n .sort((left, right) => (right.path?.length ?? 0) - (left.path?.length ?? 0))[0];\n if (byPath && byPath.path !== '/') return { tenant: byPath, matchedBy: 'path' };\n\n if (allowHeader && input.header) {\n const headerId = input.header.trim().toLowerCase();\n const tenant = byId.get(headerId);\n if (tenant?.enabled) return { tenant, matchedBy: 'header' };\n }\n\n if (byPath) return { tenant: byPath, matchedBy: 'path' };\n return undefined;\n },\n summary() {\n return tenants.map((tenant) => {\n const database = tenant.options.databaseOptions as {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n return {\n id: tenant.id,\n hosts: [...tenant.hosts],\n ...(tenant.path ? { path: tenant.path } : {}),\n enabled: tenant.enabled,\n ...(tenant.description ? { description: tenant.description } : {}),\n ...(tenant.port ? { port: tenant.port } : {}),\n file: tenant.file,\n database: {\n host: database.host,\n port: database.port,\n database: database.database,\n user: database.user,\n },\n issuer: tenant.options.jwtOptions.issuer,\n ...(tenant.options.jwtOptions.audience\n ? {\n audience: typeof tenant.options.jwtOptions.audience === 'string'\n ? tenant.options.jwtOptions.audience\n : tenant.options.jwtOptions.audience.join(','),\n }\n : {}),\n jwtAlgorithm: tenant.options.jwtOptions.algorithm ?? 'HS256',\n refreshEnabled: Boolean(tenant.options.jwtOptions.refreshExpiresIn),\n };\n });\n },\n };\n\n return registry;\n}\n"]}