emilsoftware-utilities 2.0.0-dev.36 → 2.0.0-dev.38

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 (71) hide show
  1. package/dist/accessi-module/AccessiModule.d.ts +17 -1
  2. package/dist/accessi-module/AccessiModule.js +8 -4
  3. package/dist/accessi-module/AccessiModule.js.map +1 -1
  4. package/dist/accessi-module/Console/console.css +12 -0
  5. package/dist/accessi-module/Console/console.js +99 -5
  6. package/dist/accessi-module/Console/console.js.map +1 -1
  7. package/dist/accessi-module/Console/generated/accessiApi.d.ts +21 -1
  8. package/dist/accessi-module/Console/generated/accessiApi.js +19 -3
  9. package/dist/accessi-module/Console/generated/accessiApi.js.map +1 -1
  10. package/dist/accessi-module/Console/generated/model/bootstrapAdminRequest.d.ts +17 -0
  11. package/dist/accessi-module/Console/generated/model/bootstrapAdminRequest.js +9 -0
  12. package/dist/accessi-module/Console/generated/model/bootstrapAdminRequest.js.map +1 -0
  13. package/dist/accessi-module/Console/generated/model/bootstrapAdminResponse.d.ts +14 -0
  14. package/dist/accessi-module/Console/generated/model/bootstrapAdminResponse.js +3 -0
  15. package/dist/accessi-module/Console/generated/model/bootstrapAdminResponse.js.map +1 -0
  16. package/dist/accessi-module/Console/generated/model/bootstrapAdminResultDto.d.ts +13 -0
  17. package/dist/accessi-module/Console/generated/model/bootstrapAdminResultDto.js +9 -0
  18. package/dist/accessi-module/Console/generated/model/bootstrapAdminResultDto.js.map +1 -0
  19. package/dist/accessi-module/Console/generated/model/getSchemaResponse.d.ts +14 -0
  20. package/dist/accessi-module/Console/generated/model/getSchemaResponse.js +3 -0
  21. package/dist/accessi-module/Console/generated/model/getSchemaResponse.js.map +1 -0
  22. package/dist/accessi-module/Console/generated/model/index.d.ts +6 -0
  23. package/dist/accessi-module/Console/generated/model/index.js +6 -0
  24. package/dist/accessi-module/Console/generated/model/index.js.map +1 -1
  25. package/dist/accessi-module/Console/generated/model/schemaDdlResult.d.ts +20 -0
  26. package/dist/accessi-module/Console/generated/model/schemaDdlResult.js +3 -0
  27. package/dist/accessi-module/Console/generated/model/schemaDdlResult.js.map +1 -0
  28. package/dist/accessi-module/Console/generated/model/schemaDdlSection.d.ts +12 -0
  29. package/dist/accessi-module/Console/generated/model/schemaDdlSection.js +9 -0
  30. package/dist/accessi-module/Console/generated/model/schemaDdlSection.js.map +1 -0
  31. package/dist/accessi-module/Console/index.html +20 -0
  32. package/dist/accessi-module/Console/wiki.js +5 -0
  33. package/dist/accessi-module/Console/wiki.js.map +1 -1
  34. package/dist/accessi-module/Controllers/AuthController.d.ts +5 -2
  35. package/dist/accessi-module/Controllers/AuthController.js +39 -3
  36. package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
  37. package/dist/accessi-module/Controllers/SchemaController.d.ts +9 -0
  38. package/dist/accessi-module/Controllers/SchemaController.js +79 -0
  39. package/dist/accessi-module/Controllers/SchemaController.js.map +1 -0
  40. package/dist/accessi-module/Dtos/AdminBootstrapDtos.d.ts +19 -0
  41. package/dist/accessi-module/Dtos/AdminBootstrapDtos.js +83 -0
  42. package/dist/accessi-module/Dtos/AdminBootstrapDtos.js.map +1 -0
  43. package/dist/accessi-module/Dtos/SchemaDtos.d.ts +19 -0
  44. package/dist/accessi-module/Dtos/SchemaDtos.js +84 -0
  45. package/dist/accessi-module/Dtos/SchemaDtos.js.map +1 -0
  46. package/dist/accessi-module/Dtos/index.d.ts +2 -0
  47. package/dist/accessi-module/Dtos/index.js +2 -0
  48. package/dist/accessi-module/Dtos/index.js.map +1 -1
  49. package/dist/accessi-module/Services/AdminBootstrapService/AdminBootstrapService.d.ts +33 -0
  50. package/dist/accessi-module/Services/AdminBootstrapService/AdminBootstrapService.js +106 -0
  51. package/dist/accessi-module/Services/AdminBootstrapService/AdminBootstrapService.js.map +1 -0
  52. package/dist/accessi-module/Services/SchemaExportService/SchemaExportService.d.ts +36 -0
  53. package/dist/accessi-module/Services/SchemaExportService/SchemaExportService.js +125 -0
  54. package/dist/accessi-module/Services/SchemaExportService/SchemaExportService.js.map +1 -0
  55. package/dist/accessi-module/index.d.ts +5 -0
  56. package/dist/accessi-module/index.js +12 -1
  57. package/dist/accessi-module/index.js.map +1 -1
  58. package/dist/accessi-module/security/adminBootstrap.d.ts +8 -0
  59. package/dist/accessi-module/security/adminBootstrap.js +41 -0
  60. package/dist/accessi-module/security/adminBootstrap.js.map +1 -0
  61. package/dist/accessi-module/security/publicAuthRateLimit.d.ts +1 -1
  62. package/dist/accessi-module/security/publicAuthRateLimit.js +1 -0
  63. package/dist/accessi-module/security/publicAuthRateLimit.js.map +1 -1
  64. package/dist/accessi-module/startupLog.d.ts +6 -0
  65. package/dist/accessi-module/startupLog.js +25 -0
  66. package/dist/accessi-module/startupLog.js.map +1 -0
  67. package/dist/emilsoftware-utilities-2.0.0-dev.38.tgz +0 -0
  68. package/dist/initEmilsoftwareModule.js +2 -0
  69. package/dist/initEmilsoftwareModule.js.map +1 -1
  70. package/package.json +1 -1
  71. package/dist/emilsoftware-utilities-2.0.0-dev.36.tgz +0 -0
@@ -0,0 +1,36 @@
1
+ import type { AccessiOptions } from '../../AccessiModule';
2
+ export interface SchemaDdlSection {
3
+ id: string;
4
+ title: string;
5
+ sql: string;
6
+ }
7
+ export interface SchemaDdlResult {
8
+ version: string;
9
+ sections: SchemaDdlSection[];
10
+ script: string;
11
+ }
12
+ export interface SchemaEntitiesResult {
13
+ presentTables: string[];
14
+ extraTables: string[];
15
+ canonicalTables: string[];
16
+ presentGenerators: string[];
17
+ }
18
+ /**
19
+ * Genera il DDL delle entita Accessi a partire dallo schema canonico (stessa fonte della
20
+ * riconciliazione) e rileva le entita effettivamente presenti nel database. Serve a
21
+ * esportare lo schema per replicarlo su altre installazioni.
22
+ */
23
+ export declare class SchemaExportService {
24
+ private readonly options;
25
+ constructor(options: AccessiOptions);
26
+ /** DDL completo e deterministico dello schema Accessi. */
27
+ buildDdl(): SchemaDdlResult;
28
+ /** Elenca le tabelle/generatori presenti nel database e segnala quelli fuori schema canonical. */
29
+ presentEntities(): Promise<SchemaEntitiesResult>;
30
+ private buildTablesSection;
31
+ private buildGeneratorsSection;
32
+ private buildForeignKeysSection;
33
+ private buildIndexesSection;
34
+ private buildChecksSection;
35
+ private buildTriggersSection;
36
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.SchemaExportService = void 0;
25
+ const common_1 = require("@nestjs/common");
26
+ const Orm_1 = require("../../../Orm");
27
+ const Utilities_1 = require("../../../Utilities");
28
+ const accessiSchema_1 = require("../../database-updates/accessiSchema");
29
+ /**
30
+ * Genera il DDL delle entita Accessi a partire dallo schema canonico (stessa fonte della
31
+ * riconciliazione) e rileva le entita effettivamente presenti nel database. Serve a
32
+ * esportare lo schema per replicarlo su altre installazioni.
33
+ */
34
+ let SchemaExportService = class SchemaExportService {
35
+ constructor(options) {
36
+ this.options = options;
37
+ }
38
+ /** DDL completo e deterministico dello schema Accessi. */
39
+ buildDdl() {
40
+ const sections = [
41
+ this.buildTablesSection(),
42
+ this.buildGeneratorsSection(),
43
+ this.buildForeignKeysSection(),
44
+ this.buildIndexesSection(),
45
+ this.buildChecksSection(),
46
+ this.buildTriggersSection(),
47
+ ];
48
+ const script = sections
49
+ .map((section) => `-- ============================================================\n-- ${section.title}\n-- ============================================================\n${section.sql}`)
50
+ .join('\n\n');
51
+ return { version: accessiSchema_1.ACCESSI_SCHEMA_VERSION, sections, script };
52
+ }
53
+ /** Elenca le tabelle/generatori presenti nel database e segnala quelli fuori schema canonical. */
54
+ presentEntities() {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ const canonicalTables = Object.keys(accessiSchema_1.accessiTables).sort();
57
+ const canonicalSet = new Set(canonicalTables);
58
+ const tableRows = (yield Orm_1.Orm.query(this.options.databaseOptions, `SELECT TRIM(RDB$RELATION_NAME) AS NAME
59
+ FROM RDB$RELATIONS
60
+ WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0 AND RDB$VIEW_BLR IS NULL
61
+ ORDER BY RDB$RELATION_NAME`, [], false));
62
+ const presentTables = tableRows
63
+ .map((row) => { var _a; return String((_a = Utilities_1.RestUtilities.convertKeysToCamelCase(row).name) !== null && _a !== void 0 ? _a : '').trim(); })
64
+ .filter(Boolean);
65
+ const generatorRows = (yield Orm_1.Orm.query(this.options.databaseOptions, `SELECT TRIM(RDB$GENERATOR_NAME) AS NAME
66
+ FROM RDB$GENERATORS
67
+ WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0
68
+ ORDER BY RDB$GENERATOR_NAME`, [], false));
69
+ const presentGenerators = generatorRows
70
+ .map((row) => { var _a; return String((_a = Utilities_1.RestUtilities.convertKeysToCamelCase(row).name) !== null && _a !== void 0 ? _a : '').trim(); })
71
+ .filter(Boolean);
72
+ return {
73
+ presentTables,
74
+ extraTables: presentTables.filter((name) => !canonicalSet.has(name)),
75
+ canonicalTables,
76
+ presentGenerators,
77
+ };
78
+ });
79
+ }
80
+ buildTablesSection() {
81
+ const statements = [];
82
+ for (const [table, schema] of Object.entries(accessiSchema_1.accessiTables)) {
83
+ const columns = Object.entries(schema.columns)
84
+ .map(([name, definition]) => ` ${name} ${definition}`)
85
+ .join(',\n');
86
+ statements.push(`CREATE TABLE ${table} (\n${columns}\n);`);
87
+ statements.push(`ALTER TABLE ${table} ADD CONSTRAINT PK_${table} PRIMARY KEY (${schema.primaryKey.join(', ')});`);
88
+ }
89
+ return { id: 'tables', title: `Tabelle (${Object.keys(accessiSchema_1.accessiTables).length})`, sql: statements.join('\n\n') };
90
+ }
91
+ buildGeneratorsSection() {
92
+ const statements = accessiSchema_1.accessiGenerators.map((generator) => `-- Firebird 2.5: CREATE GENERATOR ${generator.name}\nCREATE SEQUENCE ${generator.name};`);
93
+ return { id: 'generators', title: `Generatori/Sequenze (${accessiSchema_1.accessiGenerators.length})`, sql: statements.join('\n\n') };
94
+ }
95
+ buildForeignKeysSection() {
96
+ const statements = accessiSchema_1.accessiForeignKeys.map((fk) => `ALTER TABLE ${fk.table} ADD CONSTRAINT ${fk.name} FOREIGN KEY (${fk.column}) REFERENCES ${fk.target} (${fk.targetColumn})${fk.cascade ? ' ON DELETE CASCADE' : ''};`);
97
+ return { id: 'foreignKeys', title: `Chiavi esterne (${accessiSchema_1.accessiForeignKeys.length})`, sql: statements.join('\n\n') };
98
+ }
99
+ buildIndexesSection() {
100
+ const statements = accessiSchema_1.accessiIndexes.map((index) => `CREATE INDEX ${index.name} ON ${index.table} (${index.columns.join(', ')});`);
101
+ return { id: 'indexes', title: `Indici (${accessiSchema_1.accessiIndexes.length})`, sql: statements.join('\n\n') };
102
+ }
103
+ buildChecksSection() {
104
+ const statements = accessiSchema_1.accessiChecks.map((check) => `ALTER TABLE ${check.table} ADD CONSTRAINT ${check.name} CHECK (${check.expression});`);
105
+ return { id: 'checks', title: `Vincoli CHECK (${accessiSchema_1.accessiChecks.length})`, sql: statements.join('\n\n') };
106
+ }
107
+ buildTriggersSection() {
108
+ if (accessiSchema_1.accessiTriggers.length === 0) {
109
+ return { id: 'triggers', title: 'Trigger (0)', sql: '-- Nessun trigger di dominio.' };
110
+ }
111
+ const statements = accessiSchema_1.accessiTriggers.map((trigger) => `CREATE TRIGGER ${trigger.name} FOR ${trigger.table} ACTIVE ${trigger.event} ${trigger.body}`);
112
+ return {
113
+ id: 'triggers',
114
+ title: `Trigger (${accessiSchema_1.accessiTriggers.length})`,
115
+ sql: `SET TERM ^ ;\n\n${statements.join('^\n\n')}^\n\nSET TERM ; ^`,
116
+ };
117
+ }
118
+ };
119
+ exports.SchemaExportService = SchemaExportService;
120
+ exports.SchemaExportService = SchemaExportService = __decorate([
121
+ (0, common_1.Injectable)(),
122
+ __param(0, (0, common_1.Inject)('ACCESSI_OPTIONS')),
123
+ __metadata("design:paramtypes", [Object])
124
+ ], SchemaExportService);
125
+ //# sourceMappingURL=SchemaExportService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaExportService.js","sourceRoot":"","sources":["../../../../src/accessi-module/Services/SchemaExportService/SchemaExportService.ts"],"names":[],"mappings":"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","sourcesContent":["import { Inject, Injectable } from '@nestjs/common';\nimport { Orm } from '../../../Orm';\nimport { RestUtilities } from '../../../Utilities';\nimport type { AccessiOptions } from '../../AccessiModule';\nimport {\n ACCESSI_SCHEMA_VERSION,\n accessiTables,\n accessiForeignKeys,\n accessiIndexes,\n accessiChecks,\n accessiTriggers,\n accessiGenerators,\n} from '../../database-updates/accessiSchema';\n\nexport interface SchemaDdlSection {\n id: string;\n title: string;\n sql: string;\n}\n\nexport interface SchemaDdlResult {\n version: string;\n sections: SchemaDdlSection[];\n script: string;\n}\n\nexport interface SchemaEntitiesResult {\n presentTables: string[];\n extraTables: string[];\n canonicalTables: string[];\n presentGenerators: string[];\n}\n\n/**\n * Genera il DDL delle entita Accessi a partire dallo schema canonico (stessa fonte della\n * riconciliazione) e rileva le entita effettivamente presenti nel database. Serve a\n * esportare lo schema per replicarlo su altre installazioni.\n */\n@Injectable()\nexport class SchemaExportService {\n constructor(@Inject('ACCESSI_OPTIONS') private readonly options: AccessiOptions) {}\n\n /** DDL completo e deterministico dello schema Accessi. */\n buildDdl(): SchemaDdlResult {\n const sections: SchemaDdlSection[] = [\n this.buildTablesSection(),\n this.buildGeneratorsSection(),\n this.buildForeignKeysSection(),\n this.buildIndexesSection(),\n this.buildChecksSection(),\n this.buildTriggersSection(),\n ];\n\n const script = sections\n .map((section) => `-- ============================================================\\n-- ${section.title}\\n-- ============================================================\\n${section.sql}`)\n .join('\\n\\n');\n\n return { version: ACCESSI_SCHEMA_VERSION, sections, script };\n }\n\n /** Elenca le tabelle/generatori presenti nel database e segnala quelli fuori schema canonical. */\n async presentEntities(): Promise<SchemaEntitiesResult> {\n const canonicalTables = Object.keys(accessiTables).sort();\n const canonicalSet = new Set(canonicalTables);\n\n const tableRows = (await Orm.query(\n this.options.databaseOptions,\n `SELECT TRIM(RDB$RELATION_NAME) AS NAME\n FROM RDB$RELATIONS\n WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0 AND RDB$VIEW_BLR IS NULL\n ORDER BY RDB$RELATION_NAME`,\n [],\n false,\n )) as Array<Record<string, unknown>>;\n const presentTables = tableRows\n .map((row) => String(RestUtilities.convertKeysToCamelCase(row).name ?? '').trim())\n .filter(Boolean);\n\n const generatorRows = (await Orm.query(\n this.options.databaseOptions,\n `SELECT TRIM(RDB$GENERATOR_NAME) AS NAME\n FROM RDB$GENERATORS\n WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0\n ORDER BY RDB$GENERATOR_NAME`,\n [],\n false,\n )) as Array<Record<string, unknown>>;\n const presentGenerators = generatorRows\n .map((row) => String(RestUtilities.convertKeysToCamelCase(row).name ?? '').trim())\n .filter(Boolean);\n\n return {\n presentTables,\n extraTables: presentTables.filter((name) => !canonicalSet.has(name)),\n canonicalTables,\n presentGenerators,\n };\n }\n\n private buildTablesSection(): SchemaDdlSection {\n const statements: string[] = [];\n for (const [table, schema] of Object.entries(accessiTables)) {\n const columns = Object.entries(schema.columns)\n .map(([name, definition]) => ` ${name} ${definition}`)\n .join(',\\n');\n statements.push(`CREATE TABLE ${table} (\\n${columns}\\n);`);\n statements.push(`ALTER TABLE ${table} ADD CONSTRAINT PK_${table} PRIMARY KEY (${schema.primaryKey.join(', ')});`);\n }\n return { id: 'tables', title: `Tabelle (${Object.keys(accessiTables).length})`, sql: statements.join('\\n\\n') };\n }\n\n private buildGeneratorsSection(): SchemaDdlSection {\n const statements = accessiGenerators.map(\n (generator) =>\n `-- Firebird 2.5: CREATE GENERATOR ${generator.name}\\nCREATE SEQUENCE ${generator.name};`,\n );\n return { id: 'generators', title: `Generatori/Sequenze (${accessiGenerators.length})`, sql: statements.join('\\n\\n') };\n }\n\n private buildForeignKeysSection(): SchemaDdlSection {\n const statements = accessiForeignKeys.map(\n (fk) =>\n `ALTER TABLE ${fk.table} ADD CONSTRAINT ${fk.name} FOREIGN KEY (${fk.column}) REFERENCES ${fk.target} (${fk.targetColumn})${fk.cascade ? ' ON DELETE CASCADE' : ''};`,\n );\n return { id: 'foreignKeys', title: `Chiavi esterne (${accessiForeignKeys.length})`, sql: statements.join('\\n\\n') };\n }\n\n private buildIndexesSection(): SchemaDdlSection {\n const statements = accessiIndexes.map(\n (index) => `CREATE INDEX ${index.name} ON ${index.table} (${index.columns.join(', ')});`,\n );\n return { id: 'indexes', title: `Indici (${accessiIndexes.length})`, sql: statements.join('\\n\\n') };\n }\n\n private buildChecksSection(): SchemaDdlSection {\n const statements = accessiChecks.map(\n (check) => `ALTER TABLE ${check.table} ADD CONSTRAINT ${check.name} CHECK (${check.expression});`,\n );\n return { id: 'checks', title: `Vincoli CHECK (${accessiChecks.length})`, sql: statements.join('\\n\\n') };\n }\n\n private buildTriggersSection(): SchemaDdlSection {\n if (accessiTriggers.length === 0) {\n return { id: 'triggers', title: 'Trigger (0)', sql: '-- Nessun trigger di dominio.' };\n }\n const statements = accessiTriggers.map(\n (trigger) => `CREATE TRIGGER ${trigger.name} FOR ${trigger.table} ACTIVE ${trigger.event} ${trigger.body}`,\n );\n return {\n id: 'triggers',\n title: `Trigger (${accessiTriggers.length})`,\n sql: `SET TERM ^ ;\\n\\n${statements.join('^\\n\\n')}^\\n\\nSET TERM ; ^`,\n };\n }\n}\n"]}
@@ -21,6 +21,11 @@ export { AccessiCatalogMigrator, applyCatalogScripts, listPendingCatalogScripts,
21
21
  export type { CatalogMigrationReport, CatalogScriptMigration } from "./database-updates/AccessiCatalogMigrator";
22
22
  export { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';
23
23
  export type { ServiceTokenMetadata, IssuedServiceToken, VerifiedServiceToken, IssueServiceTokenInput } from './Services/ServiceTokenService/ServiceTokenService';
24
+ export { SchemaExportService } from './Services/SchemaExportService/SchemaExportService';
25
+ export type { SchemaDdlResult, SchemaDdlSection, SchemaEntitiesResult } from './Services/SchemaExportService/SchemaExportService';
26
+ export { AdminBootstrapService } from './Services/AdminBootstrapService/AdminBootstrapService';
27
+ export type { BootstrapAdminResult } from './Services/AdminBootstrapService/AdminBootstrapService';
28
+ export { getOrCreateAdminBootstrapToken, getAdminBootstrapToken, clearAdminBootstrapToken, isValidAdminBootstrapToken } from './security/adminBootstrap';
24
29
  export { ServiceTokenGuard, RequireServiceTokenScopes, getAccessiServiceToken } from './security/serviceTokenGuard';
25
30
  export { assertEmailConfigured, isEmailConfigured, AccessiEmailNotConfiguredError, ACCESSI_EMAIL_NOT_CONFIGURED } from './security/emailConfiguration';
26
31
  export { FederatedAuthService } from './federated-auth/FederatedAuthService';
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.ACCESSI_MODULE_SERVICES_LOCALS_KEY = exports.getAccessiModuleServices = exports.registerAccessiModuleServices = exports.accessiRequirement = exports.authenticateGen = exports.authorizeAccessi = exports.FederatedAuthService = exports.ACCESSI_EMAIL_NOT_CONFIGURED = exports.AccessiEmailNotConfiguredError = exports.isEmailConfigured = exports.assertEmailConfigured = exports.getAccessiServiceToken = exports.RequireServiceTokenScopes = exports.ServiceTokenGuard = exports.ServiceTokenService = exports.discoverCatalogScripts = exports.computeCatalogScriptChecksum = exports.leadingStatementKeyword = exports.analyzeCatalogScript = exports.splitSqlStatements = exports.listPendingCatalogScripts = exports.applyCatalogScripts = exports.AccessiCatalogMigrator = exports.AccessiDatabaseUpdater = exports.AccessiModule = void 0;
29
+ exports.ACCESSI_MODULE_SERVICES_LOCALS_KEY = exports.getAccessiModuleServices = exports.registerAccessiModuleServices = exports.accessiRequirement = exports.authenticateGen = exports.authorizeAccessi = exports.FederatedAuthService = exports.ACCESSI_EMAIL_NOT_CONFIGURED = exports.AccessiEmailNotConfiguredError = exports.isEmailConfigured = exports.assertEmailConfigured = exports.getAccessiServiceToken = exports.RequireServiceTokenScopes = exports.ServiceTokenGuard = exports.isValidAdminBootstrapToken = exports.clearAdminBootstrapToken = exports.getAdminBootstrapToken = exports.getOrCreateAdminBootstrapToken = exports.AdminBootstrapService = exports.SchemaExportService = exports.ServiceTokenService = exports.discoverCatalogScripts = exports.computeCatalogScriptChecksum = exports.leadingStatementKeyword = exports.analyzeCatalogScript = exports.splitSqlStatements = exports.listPendingCatalogScripts = exports.applyCatalogScripts = exports.AccessiCatalogMigrator = exports.AccessiDatabaseUpdater = exports.AccessiModule = void 0;
30
30
  exports.initializeAccessiModule = initializeAccessiModule;
31
31
  const express_1 = __importDefault(require("express"));
32
32
  const core_1 = require("@nestjs/core");
@@ -37,6 +37,7 @@ const AuthService_1 = require("./Services/AuthService/AuthService");
37
37
  const SwaggerConfig_1 = require("../swagger/SwaggerConfig");
38
38
  const authenticateGen_1 = require("./middleware/authenticateGen");
39
39
  const accessiRuntime_1 = require("./accessiRuntime");
40
+ const startupLog_1 = require("./startupLog");
40
41
  const accessiValidation_1 = require("./security/accessiValidation");
41
42
  const AccessiHttpExceptionFilter_1 = require("./security/AccessiHttpExceptionFilter");
42
43
  const emailConfiguration_1 = require("./security/emailConfiguration");
@@ -109,6 +110,7 @@ function initializeAccessiModule(app, options) {
109
110
  const accessiServices = (0, accessiRuntime_1.registerAccessiModuleServices)(app, nestApp);
110
111
  app.locals.accessiAuthService = accessiServices.authenticateGenService;
111
112
  (0, authenticateGen_1.setAccessiAuthService)(accessiServices.authenticateGenService);
113
+ (0, startupLog_1.logAccessiStartup)(options);
112
114
  const elapsedMs = performance.now() - startedAt;
113
115
  logger.info(`Accessi initialized. Tempo totale bootstrap: ${elapsedMs.toFixed(2)} ms`);
114
116
  }
@@ -136,6 +138,15 @@ Object.defineProperty(exports, "computeCatalogScriptChecksum", { enumerable: tru
136
138
  Object.defineProperty(exports, "discoverCatalogScripts", { enumerable: true, get: function () { return AccessiCatalogMigrator_1.discoverCatalogScripts; } });
137
139
  var ServiceTokenService_1 = require("./Services/ServiceTokenService/ServiceTokenService");
138
140
  Object.defineProperty(exports, "ServiceTokenService", { enumerable: true, get: function () { return ServiceTokenService_1.ServiceTokenService; } });
141
+ var SchemaExportService_1 = require("./Services/SchemaExportService/SchemaExportService");
142
+ Object.defineProperty(exports, "SchemaExportService", { enumerable: true, get: function () { return SchemaExportService_1.SchemaExportService; } });
143
+ var AdminBootstrapService_1 = require("./Services/AdminBootstrapService/AdminBootstrapService");
144
+ Object.defineProperty(exports, "AdminBootstrapService", { enumerable: true, get: function () { return AdminBootstrapService_1.AdminBootstrapService; } });
145
+ var adminBootstrap_1 = require("./security/adminBootstrap");
146
+ Object.defineProperty(exports, "getOrCreateAdminBootstrapToken", { enumerable: true, get: function () { return adminBootstrap_1.getOrCreateAdminBootstrapToken; } });
147
+ Object.defineProperty(exports, "getAdminBootstrapToken", { enumerable: true, get: function () { return adminBootstrap_1.getAdminBootstrapToken; } });
148
+ Object.defineProperty(exports, "clearAdminBootstrapToken", { enumerable: true, get: function () { return adminBootstrap_1.clearAdminBootstrapToken; } });
149
+ Object.defineProperty(exports, "isValidAdminBootstrapToken", { enumerable: true, get: function () { return adminBootstrap_1.isValidAdminBootstrapToken; } });
139
150
  var serviceTokenGuard_1 = require("./security/serviceTokenGuard");
140
151
  Object.defineProperty(exports, "ServiceTokenGuard", { enumerable: true, get: function () { return serviceTokenGuard_1.ServiceTokenGuard; } });
141
152
  Object.defineProperty(exports, "RequireServiceTokenScopes", { enumerable: true, get: function () { return serviceTokenGuard_1.RequireServiceTokenScopes; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accessi-module/index.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport { AccessiModule, AccessiOptions } from \"./AccessiModule\";\nimport { Logger } from \"../Logger\";\nimport { AuthService } from \"./Services/AuthService/AuthService\";\nimport { setupSwagger } from \"../swagger/SwaggerConfig\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessiRuntime\";\nimport { createAccessiValidationPipe } from './security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './security/AccessiHttpExceptionFilter';\nimport { assertEmailConfigured } from './security/emailConfiguration';\n\nfunction describeDatabaseTarget(options: AccessiOptions): string {\n const dbOptions = options.databaseOptions as {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n\n return `${dbOptions.host ?? \"?\"}:${dbOptions.port ?? \"?\"} -> ${dbOptions.database ?? \"?\"} as ${dbOptions.user ?? \"?\"}`;\n}\n\n/**\n * Monta il modulo Accessi in un'applicazione Express gia esistente.\n *\n * Il bootstrap crea un host Nest isolato per le rotte `/api/accessi`, inizializza Swagger,\n * validazione ed error handler, quindi rende disponibile `authorizeAccessi` al resto dell'app.\n * Attendere questa Promise prima di aprire la porta HTTP: durante il bootstrap il middleware\n * restituisce `ACCESSI_AUTH_INITIALIZING` invece di autorizzare richieste parzialmente inizializzate.\n *\n * @param app Applicazione Express ospitante. Non viene sostituita ne vengono intercettate rotte estranee ad Accessi.\n * @param options Configurazione riservata del modulo; proviene normalmente da variabili d'ambiente o secret manager.\n */\nexport async function initializeAccessiModule(app: Application, options: AccessiOptions): Promise<void> {\n // Il servizio email e obbligatorio: reset password, 2FA e creazione utente ne dipendono.\n assertEmailConfigured(options);\n const logger: Logger = new Logger(\"initializeAccessiModule\");\n beginAccessiAuthInitialization();\n const startedAt = performance.now();\n\n try {\n logger.info(\"Inizializzazione modulo accessi avviata.\");\n logger.info(`Target database accessi: ${describeDatabaseTarget(options)}`);\n logger.info(\n `Configurazione accessi: autoUpdateDatabase=${options.autoUpdateDatabase !== false}, legacyPasswordMigrationOnStartup=${options.legacyPasswordMigrationOnStartup !== false}`\n );\n const nestHostApp = express();\n const isNestRoute = (path: string) =>\n path === \"/api/accessi\" ||\n path.startsWith(\"/api/accessi/\") ||\n path === \"/accessi/swagger\" ||\n path.startsWith(\"/accessi/swagger/\") ||\n path === \"/accessi/swagger.json\" ||\n path === \"/accessi/swagger-json\";\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n\n return nestHostApp(req, res, next);\n });\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n\n const nestApp = await NestFactory.create(AccessiModule.forRoot(options), nestExpressInstance, {\n bufferLogs: true\n });\n logger.info(\"Applicazione Nest accessi creata.\");\n\n nestApp.enableCors({ exposedHeaders: ['X-Total-Count'] });\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n\n nestApp.setGlobalPrefix('api', {\n exclude: ['/swagger', '/swagger/(.*)']\n });\n\n setupSwagger(nestApp, {\n swaggerPath: \"accessi/swagger\",\n swaggerJsonPath: \"accessi/swagger.json\",\n title: \"Accessi API Documentation\",\n description: \"API del modulo accessi\",\n });\n logger.info(\"Avvio init del modulo accessi.\");\n await nestApp.init();\n logger.info(\"Init del modulo accessi completata.\");\n if (options.legacyPasswordMigrationOnStartup !== false) {\n logger.info(\"Avvio migrazione password legacy accessi.\");\n const passwordMigrationService = nestApp.get(AuthService);\n await passwordMigrationService.migrateLegacyEncryptedPasswords();\n logger.info(\"Migrazione password legacy accessi completata.\");\n }\n\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n const elapsedMs = performance.now() - startedAt;\n logger.info(`Accessi initialized. Tempo totale bootstrap: ${elapsedMs.toFixed(2)} ms`);\n\n } catch (error) {\n failAccessiAuthInitialization(error);\n logger.error(\n \"Errore in initialize AccessiModule:\",\n error instanceof Error\n ? { name: error.name, message: error.message, stack: error.stack }\n : { error: String(error) }\n );\n throw error;\n }\n}\n\nexport { AccessiModule } from \"./AccessiModule\";\nexport { AccessiDatabaseUpdater } from \"./database-updates/AccessiDatabaseUpdater\";\nexport type { AccessiOptions, EmailOptions, JwtOptions, ExtensionFieldsOptions, PublicAuthRateLimitOptions, PublicAuthRateLimitRuleOptions } from \"./AccessiModule\";\nexport type { PublicRegistrationOptions, FederatedAuthenticationOptions, ServiceTokenOptions } from \"./AccessiModule\";\nexport type { CatalogScriptsOptions, CatalogScriptChecksumPolicy } from \"./AccessiModule\";\nexport {\n AccessiCatalogMigrator,\n applyCatalogScripts,\n listPendingCatalogScripts,\n splitSqlStatements,\n analyzeCatalogScript,\n leadingStatementKeyword,\n computeCatalogScriptChecksum,\n discoverCatalogScripts\n} from \"./database-updates/AccessiCatalogMigrator\";\nexport type { CatalogMigrationReport, CatalogScriptMigration } from \"./database-updates/AccessiCatalogMigrator\";\nexport { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';\nexport type {\n ServiceTokenMetadata,\n IssuedServiceToken,\n VerifiedServiceToken,\n IssueServiceTokenInput\n} from './Services/ServiceTokenService/ServiceTokenService';\nexport { ServiceTokenGuard, RequireServiceTokenScopes, getAccessiServiceToken } from './security/serviceTokenGuard';\nexport { assertEmailConfigured, isEmailConfigured, AccessiEmailNotConfiguredError, ACCESSI_EMAIL_NOT_CONFIGURED } from './security/emailConfiguration';\nexport { FederatedAuthService } from './federated-auth/FederatedAuthService';\nexport type { FederatedAuthenticationResult, FederatedIdentity, FederatedProvider, VerifiedFederatedIdentity } from './federated-auth/FederatedAuthTypes';\nexport * from \"./Dtos\";\nexport {\n authorizeAccessi,\n authenticateGen\n} from \"./middleware/authenticateGen\";\nexport { accessiRequirement } from \"./middleware/accessiRequirements\";\nexport {\n registerAccessiModuleServices,\n getAccessiModuleServices,\n ACCESSI_MODULE_SERVICES_LOCALS_KEY\n} from \"./accessiRuntime\";\nexport type { AccessiModuleServices } from \"./accessiRuntime\";\nexport type {\n AccessiAuthorizationOptions,\n AccessiRequirementNode,\n AccessiCustomRequirementContext,\n AccessiCustomRequirementHandler\n} from \"./middleware/accessiRequirements\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/accessi-module/index.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport { AccessiModule, AccessiOptions } from \"./AccessiModule\";\nimport { Logger } from \"../Logger\";\nimport { AuthService } from \"./Services/AuthService/AuthService\";\nimport { setupSwagger } from \"../swagger/SwaggerConfig\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessiRuntime\";\nimport { logAccessiStartup } from \"./startupLog\";\nimport { createAccessiValidationPipe } from './security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './security/AccessiHttpExceptionFilter';\nimport { assertEmailConfigured } from './security/emailConfiguration';\n\nfunction describeDatabaseTarget(options: AccessiOptions): string {\n const dbOptions = options.databaseOptions as {\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n };\n\n return `${dbOptions.host ?? \"?\"}:${dbOptions.port ?? \"?\"} -> ${dbOptions.database ?? \"?\"} as ${dbOptions.user ?? \"?\"}`;\n}\n\n/**\n * Monta il modulo Accessi in un'applicazione Express gia esistente.\n *\n * Il bootstrap crea un host Nest isolato per le rotte `/api/accessi`, inizializza Swagger,\n * validazione ed error handler, quindi rende disponibile `authorizeAccessi` al resto dell'app.\n * Attendere questa Promise prima di aprire la porta HTTP: durante il bootstrap il middleware\n * restituisce `ACCESSI_AUTH_INITIALIZING` invece di autorizzare richieste parzialmente inizializzate.\n *\n * @param app Applicazione Express ospitante. Non viene sostituita ne vengono intercettate rotte estranee ad Accessi.\n * @param options Configurazione riservata del modulo; proviene normalmente da variabili d'ambiente o secret manager.\n */\nexport async function initializeAccessiModule(app: Application, options: AccessiOptions): Promise<void> {\n // Il servizio email e obbligatorio: reset password, 2FA e creazione utente ne dipendono.\n assertEmailConfigured(options);\n const logger: Logger = new Logger(\"initializeAccessiModule\");\n beginAccessiAuthInitialization();\n const startedAt = performance.now();\n\n try {\n logger.info(\"Inizializzazione modulo accessi avviata.\");\n logger.info(`Target database accessi: ${describeDatabaseTarget(options)}`);\n logger.info(\n `Configurazione accessi: autoUpdateDatabase=${options.autoUpdateDatabase !== false}, legacyPasswordMigrationOnStartup=${options.legacyPasswordMigrationOnStartup !== false}`\n );\n const nestHostApp = express();\n const isNestRoute = (path: string) =>\n path === \"/api/accessi\" ||\n path.startsWith(\"/api/accessi/\") ||\n path === \"/accessi/swagger\" ||\n path.startsWith(\"/accessi/swagger/\") ||\n path === \"/accessi/swagger.json\" ||\n path === \"/accessi/swagger-json\";\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n\n return nestHostApp(req, res, next);\n });\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n\n const nestApp = await NestFactory.create(AccessiModule.forRoot(options), nestExpressInstance, {\n bufferLogs: true\n });\n logger.info(\"Applicazione Nest accessi creata.\");\n\n nestApp.enableCors({ exposedHeaders: ['X-Total-Count'] });\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n\n nestApp.setGlobalPrefix('api', {\n exclude: ['/swagger', '/swagger/(.*)']\n });\n\n setupSwagger(nestApp, {\n swaggerPath: \"accessi/swagger\",\n swaggerJsonPath: \"accessi/swagger.json\",\n title: \"Accessi API Documentation\",\n description: \"API del modulo accessi\",\n });\n logger.info(\"Avvio init del modulo accessi.\");\n await nestApp.init();\n logger.info(\"Init del modulo accessi completata.\");\n if (options.legacyPasswordMigrationOnStartup !== false) {\n logger.info(\"Avvio migrazione password legacy accessi.\");\n const passwordMigrationService = nestApp.get(AuthService);\n await passwordMigrationService.migrateLegacyEncryptedPasswords();\n logger.info(\"Migrazione password legacy accessi completata.\");\n }\n\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options);\n const elapsedMs = performance.now() - startedAt;\n logger.info(`Accessi initialized. Tempo totale bootstrap: ${elapsedMs.toFixed(2)} ms`);\n\n } catch (error) {\n failAccessiAuthInitialization(error);\n logger.error(\n \"Errore in initialize AccessiModule:\",\n error instanceof Error\n ? { name: error.name, message: error.message, stack: error.stack }\n : { error: String(error) }\n );\n throw error;\n }\n}\n\nexport { AccessiModule } from \"./AccessiModule\";\nexport { AccessiDatabaseUpdater } from \"./database-updates/AccessiDatabaseUpdater\";\nexport type { AccessiOptions, EmailOptions, JwtOptions, ExtensionFieldsOptions, PublicAuthRateLimitOptions, PublicAuthRateLimitRuleOptions } from \"./AccessiModule\";\nexport type { PublicRegistrationOptions, FederatedAuthenticationOptions, ServiceTokenOptions } from \"./AccessiModule\";\nexport type { CatalogScriptsOptions, CatalogScriptChecksumPolicy } from \"./AccessiModule\";\nexport {\n AccessiCatalogMigrator,\n applyCatalogScripts,\n listPendingCatalogScripts,\n splitSqlStatements,\n analyzeCatalogScript,\n leadingStatementKeyword,\n computeCatalogScriptChecksum,\n discoverCatalogScripts\n} from \"./database-updates/AccessiCatalogMigrator\";\nexport type { CatalogMigrationReport, CatalogScriptMigration } from \"./database-updates/AccessiCatalogMigrator\";\nexport { ServiceTokenService } from './Services/ServiceTokenService/ServiceTokenService';\nexport type {\n ServiceTokenMetadata,\n IssuedServiceToken,\n VerifiedServiceToken,\n IssueServiceTokenInput\n} from './Services/ServiceTokenService/ServiceTokenService';\nexport { SchemaExportService } from './Services/SchemaExportService/SchemaExportService';\nexport type { SchemaDdlResult, SchemaDdlSection, SchemaEntitiesResult } from './Services/SchemaExportService/SchemaExportService';\nexport { AdminBootstrapService } from './Services/AdminBootstrapService/AdminBootstrapService';\nexport type { BootstrapAdminResult } from './Services/AdminBootstrapService/AdminBootstrapService';\nexport {\n getOrCreateAdminBootstrapToken,\n getAdminBootstrapToken,\n clearAdminBootstrapToken,\n isValidAdminBootstrapToken\n} from './security/adminBootstrap';\nexport { ServiceTokenGuard, RequireServiceTokenScopes, getAccessiServiceToken } from './security/serviceTokenGuard';\nexport { assertEmailConfigured, isEmailConfigured, AccessiEmailNotConfiguredError, ACCESSI_EMAIL_NOT_CONFIGURED } from './security/emailConfiguration';\nexport { FederatedAuthService } from './federated-auth/FederatedAuthService';\nexport type { FederatedAuthenticationResult, FederatedIdentity, FederatedProvider, VerifiedFederatedIdentity } from './federated-auth/FederatedAuthTypes';\nexport * from \"./Dtos\";\nexport {\n authorizeAccessi,\n authenticateGen\n} from \"./middleware/authenticateGen\";\nexport { accessiRequirement } from \"./middleware/accessiRequirements\";\nexport {\n registerAccessiModuleServices,\n getAccessiModuleServices,\n ACCESSI_MODULE_SERVICES_LOCALS_KEY\n} from \"./accessiRuntime\";\nexport type { AccessiModuleServices } from \"./accessiRuntime\";\nexport type {\n AccessiAuthorizationOptions,\n AccessiRequirementNode,\n AccessiCustomRequirementContext,\n AccessiCustomRequirementHandler\n} from \"./middleware/accessiRequirements\";\n"]}
@@ -0,0 +1,8 @@
1
+ /** Genera (una sola volta per processo) il token di bootstrap admin. */
2
+ export declare function getOrCreateAdminBootstrapToken(): string;
3
+ /** Token corrente, oppure null se non ancora generato. */
4
+ export declare function getAdminBootstrapToken(): string | null;
5
+ /** Invalida il token (usato dopo un bootstrap riuscito per renderlo monouso). */
6
+ export declare function clearAdminBootstrapToken(): void;
7
+ /** Confronto a tempo costante del token candidato con quello generato. */
8
+ export declare function isValidAdminBootstrapToken(candidate: unknown): boolean;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getOrCreateAdminBootstrapToken = getOrCreateAdminBootstrapToken;
4
+ exports.getAdminBootstrapToken = getAdminBootstrapToken;
5
+ exports.clearAdminBootstrapToken = clearAdminBootstrapToken;
6
+ exports.isValidAdminBootstrapToken = isValidAdminBootstrapToken;
7
+ const crypto_1 = require("crypto");
8
+ /**
9
+ * Token di bootstrap admin, valido per il singolo processo Accessi.
10
+ * Viene generato al primo accesso (avvio del modulo) e rigenerato a ogni riavvio: non e mai
11
+ * persistito ne restituito via API, e viene stampato solo nel log del backend.
12
+ */
13
+ let processToken = null;
14
+ /** Genera (una sola volta per processo) il token di bootstrap admin. */
15
+ function getOrCreateAdminBootstrapToken() {
16
+ if (!processToken) {
17
+ processToken = (0, crypto_1.randomBytes)(32).toString('hex');
18
+ }
19
+ return processToken;
20
+ }
21
+ /** Token corrente, oppure null se non ancora generato. */
22
+ function getAdminBootstrapToken() {
23
+ return processToken;
24
+ }
25
+ /** Invalida il token (usato dopo un bootstrap riuscito per renderlo monouso). */
26
+ function clearAdminBootstrapToken() {
27
+ processToken = null;
28
+ }
29
+ /** Confronto a tempo costante del token candidato con quello generato. */
30
+ function isValidAdminBootstrapToken(candidate) {
31
+ if (!processToken || typeof candidate !== 'string' || candidate.length === 0) {
32
+ return false;
33
+ }
34
+ const expected = Buffer.from(processToken, 'utf8');
35
+ const provided = Buffer.from(candidate, 'utf8');
36
+ if (expected.length !== provided.length) {
37
+ return false;
38
+ }
39
+ return (0, crypto_1.timingSafeEqual)(expected, provided);
40
+ }
41
+ //# sourceMappingURL=adminBootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminBootstrap.js","sourceRoot":"","sources":["../../../src/accessi-module/security/adminBootstrap.ts"],"names":[],"mappings":";;AAUA,wEAKC;AAGD,wDAEC;AAGD,4DAEC;AAGD,gEAYC;AAxCD,mCAAsD;AAEtD;;;;GAIG;AACH,IAAI,YAAY,GAAkB,IAAI,CAAC;AAEvC,wEAAwE;AACxE,SAAgB,8BAA8B;IAC5C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,IAAA,oBAAW,EAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,0DAA0D;AAC1D,SAAgB,sBAAsB;IACpC,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,iFAAiF;AACjF,SAAgB,wBAAwB;IACtC,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,0EAA0E;AAC1E,SAAgB,0BAA0B,CAAC,SAAkB;IAC3D,IAAI,CAAC,YAAY,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAA,wBAAe,EAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["import { randomBytes, timingSafeEqual } from 'crypto';\n\n/**\n * Token di bootstrap admin, valido per il singolo processo Accessi.\n * Viene generato al primo accesso (avvio del modulo) e rigenerato a ogni riavvio: non e mai\n * persistito ne restituito via API, e viene stampato solo nel log del backend.\n */\nlet processToken: string | null = null;\n\n/** Genera (una sola volta per processo) il token di bootstrap admin. */\nexport function getOrCreateAdminBootstrapToken(): string {\n if (!processToken) {\n processToken = randomBytes(32).toString('hex');\n }\n return processToken;\n}\n\n/** Token corrente, oppure null se non ancora generato. */\nexport function getAdminBootstrapToken(): string | null {\n return processToken;\n}\n\n/** Invalida il token (usato dopo un bootstrap riuscito per renderlo monouso). */\nexport function clearAdminBootstrapToken(): void {\n processToken = null;\n}\n\n/** Confronto a tempo costante del token candidato con quello generato. */\nexport function isValidAdminBootstrapToken(candidate: unknown): boolean {\n if (!processToken || typeof candidate !== 'string' || candidate.length === 0) {\n return false;\n }\n\n const expected = Buffer.from(processToken, 'utf8');\n const provided = Buffer.from(candidate, 'utf8');\n if (expected.length !== provided.length) {\n return false;\n }\n\n return timingSafeEqual(expected, provided);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { Request, Response } from 'express';
2
2
  import type { AccessiOptions } from '../AccessiModule';
3
- export type PublicAuthRateLimitScope = 'login' | 'twoFactorVerify' | 'twoFactorResend' | 'register' | 'passwordResetEmail' | 'passwordResetConfirm' | 'getUserByToken';
3
+ export type PublicAuthRateLimitScope = 'login' | 'twoFactorVerify' | 'twoFactorResend' | 'register' | 'passwordResetEmail' | 'passwordResetConfirm' | 'getUserByToken' | 'adminBootstrap';
4
4
  type RateLimitDecision = {
5
5
  allowed: boolean;
6
6
  retryAfterSeconds: number;
@@ -12,6 +12,7 @@ const DEFAULT_PUBLIC_AUTH_RATE_LIMITS = {
12
12
  passwordResetEmail: { windowMs: 60 * 60 * 1000, maxAttempts: 5 },
13
13
  passwordResetConfirm: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },
14
14
  getUserByToken: { windowMs: 5 * 60 * 1000, maxAttempts: 30 },
15
+ adminBootstrap: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },
15
16
  };
16
17
  const RATE_LIMIT_BUCKETS = new Map();
17
18
  const MAX_TRACKED_BUCKETS = 20000;
@@ -1 +1 @@
1
- {"version":3,"file":"publicAuthRateLimit.js","sourceRoot":"","sources":["../../../src/accessi-module/security/publicAuthRateLimit.ts"],"names":[],"mappings":"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","sourcesContent":["import { HttpStatus } from '@nestjs/common';\nimport { Request, Response } from 'express';\nimport { RestUtilities } from '../../Utilities';\nimport type { AccessiOptions, PublicAuthRateLimitRuleOptions } from '../AccessiModule';\n\nexport type PublicAuthRateLimitScope =\n | 'login'\n | 'twoFactorVerify'\n | 'twoFactorResend'\n | 'register'\n | 'passwordResetEmail'\n | 'passwordResetConfirm'\n | 'getUserByToken';\n\ntype RateLimitBucket = {\n count: number;\n resetAt: number;\n lastSeenAt: number;\n};\n\ntype RateLimitDecision = {\n allowed: boolean;\n retryAfterSeconds: number;\n};\n\nconst DEFAULT_PUBLIC_AUTH_RATE_LIMITS: Record<\n PublicAuthRateLimitScope,\n PublicAuthRateLimitRuleOptions\n> = {\n login: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },\n twoFactorVerify: { windowMs: 15 * 60 * 1000, maxAttempts: 20 },\n twoFactorResend: { windowMs: 15 * 60 * 1000, maxAttempts: 5 },\n register: { windowMs: 60 * 60 * 1000, maxAttempts: 5 },\n passwordResetEmail: { windowMs: 60 * 60 * 1000, maxAttempts: 5 },\n passwordResetConfirm: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },\n getUserByToken: { windowMs: 5 * 60 * 1000, maxAttempts: 30 },\n};\n\nconst RATE_LIMIT_BUCKETS = new Map<string, RateLimitBucket>();\nconst MAX_TRACKED_BUCKETS = 20000;\nconst MAX_WINDOW_MS = Math.max(\n ...Object.values(DEFAULT_PUBLIC_AUTH_RATE_LIMITS).map((rule) => rule.windowMs),\n);\n\nfunction getRule(\n options: AccessiOptions,\n scope: PublicAuthRateLimitScope,\n): PublicAuthRateLimitRuleOptions {\n const configuredRule = options.publicAuthRateLimit?.[scope];\n\n if (\n configuredRule &&\n Number.isFinite(configuredRule.windowMs) &&\n configuredRule.windowMs > 0 &&\n Number.isFinite(configuredRule.maxAttempts) &&\n configuredRule.maxAttempts > 0\n ) {\n return {\n windowMs: Math.trunc(configuredRule.windowMs),\n maxAttempts: Math.trunc(configuredRule.maxAttempts),\n };\n }\n\n return DEFAULT_PUBLIC_AUTH_RATE_LIMITS[scope];\n}\n\nfunction normalizeIdentifier(identifier: string): string | null {\n if (typeof identifier !== 'string') {\n return null;\n }\n\n const normalized = identifier.trim().toLowerCase();\n return normalized === '' ? null : normalized;\n}\n\nfunction pruneRateLimitBuckets(now: number): void {\n if (RATE_LIMIT_BUCKETS.size < MAX_TRACKED_BUCKETS) {\n return;\n }\n\n for (const [key, bucket] of RATE_LIMIT_BUCKETS.entries()) {\n if (bucket.resetAt <= now || bucket.lastSeenAt <= now - MAX_WINDOW_MS * 2) {\n RATE_LIMIT_BUCKETS.delete(key);\n }\n\n if (RATE_LIMIT_BUCKETS.size < MAX_TRACKED_BUCKETS) {\n return;\n }\n }\n}\n\n/** Mantiene il numero di bucket sotto la soglia, scartando i meno recenti. */\nfunction enforceBucketCap(): void {\n while (RATE_LIMIT_BUCKETS.size >= MAX_TRACKED_BUCKETS) {\n let oldestKey: string | undefined;\n let oldestSeen = Number.POSITIVE_INFINITY;\n\n for (const [key, bucket] of RATE_LIMIT_BUCKETS.entries()) {\n if (bucket.lastSeenAt < oldestSeen) {\n oldestSeen = bucket.lastSeenAt;\n oldestKey = key;\n }\n }\n\n if (oldestKey === undefined) {\n return;\n }\n\n RATE_LIMIT_BUCKETS.delete(oldestKey);\n }\n}\n\nfunction consumeBucket(\n bucketKey: string,\n rule: PublicAuthRateLimitRuleOptions,\n now: number,\n): number | null {\n const existingBucket = RATE_LIMIT_BUCKETS.get(bucketKey);\n if (!existingBucket || existingBucket.resetAt <= now) {\n enforceBucketCap();\n RATE_LIMIT_BUCKETS.set(bucketKey, {\n count: 1,\n resetAt: now + rule.windowMs,\n lastSeenAt: now,\n });\n return null;\n }\n\n existingBucket.lastSeenAt = now;\n if (existingBucket.count >= rule.maxAttempts) {\n return Math.max(1, Math.ceil((existingBucket.resetAt - now) / 1000));\n }\n\n existingBucket.count += 1;\n return null;\n}\n\nexport function checkPublicAuthRateLimit(\n options: AccessiOptions,\n scope: PublicAuthRateLimitScope,\n req: Request,\n identifiers: string[] = [],\n): RateLimitDecision {\n if (options.publicAuthRateLimit?.enabled === false) {\n return { allowed: true, retryAfterSeconds: 0 };\n }\n\n const rule = getRule(options, scope);\n const now = Date.now();\n pruneRateLimitBuckets(now);\n\n const ipAddress =\n normalizeIdentifier(req.ip ?? req.socket?.remoteAddress ?? 'unknown') ?? 'unknown';\n\n // Consuma prima il bucket per IP: se gia bloccato non crea bucket per soggetto,\n // evitando crescita di memoria con identificatori sempre diversi.\n const ipRetryAfterSeconds = consumeBucket(`${scope}|ip|${ipAddress}`, rule, now);\n if (ipRetryAfterSeconds) {\n return { allowed: false, retryAfterSeconds: ipRetryAfterSeconds };\n }\n\n let retryAfterSeconds = 0;\n for (const identifier of identifiers) {\n const normalizedIdentifier = normalizeIdentifier(identifier);\n if (!normalizedIdentifier) {\n continue;\n }\n\n const bucketRetryAfterSeconds = consumeBucket(\n `${scope}|ip-subject|${ipAddress}|${normalizedIdentifier}`,\n rule,\n now,\n );\n if (bucketRetryAfterSeconds) {\n retryAfterSeconds = Math.max(retryAfterSeconds, bucketRetryAfterSeconds);\n }\n }\n\n if (retryAfterSeconds > 0) {\n return {\n allowed: false,\n retryAfterSeconds,\n };\n }\n\n return { allowed: true, retryAfterSeconds: 0 };\n}\n\nexport function sendPublicAuthRateLimitExceeded(\n res: Response,\n retryAfterSeconds: number,\n): Response {\n res.setHeader('Retry-After', String(retryAfterSeconds));\n return RestUtilities.sendErrorMessage(\n res,\n 'Troppi tentativi. Riprova piu tardi.',\n 'PublicAuthRateLimit',\n HttpStatus.TOO_MANY_REQUESTS,\n );\n}\n"]}
1
+ {"version":3,"file":"publicAuthRateLimit.js","sourceRoot":"","sources":["../../../src/accessi-module/security/publicAuthRateLimit.ts"],"names":[],"mappings":"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","sourcesContent":["import { HttpStatus } from '@nestjs/common';\nimport { Request, Response } from 'express';\nimport { RestUtilities } from '../../Utilities';\nimport type { AccessiOptions, PublicAuthRateLimitRuleOptions } from '../AccessiModule';\n\nexport type PublicAuthRateLimitScope =\n | 'login'\n | 'twoFactorVerify'\n | 'twoFactorResend'\n | 'register'\n | 'passwordResetEmail'\n | 'passwordResetConfirm'\n | 'getUserByToken'\n | 'adminBootstrap';\n\ntype RateLimitBucket = {\n count: number;\n resetAt: number;\n lastSeenAt: number;\n};\n\ntype RateLimitDecision = {\n allowed: boolean;\n retryAfterSeconds: number;\n};\n\nconst DEFAULT_PUBLIC_AUTH_RATE_LIMITS: Record<\n PublicAuthRateLimitScope,\n PublicAuthRateLimitRuleOptions\n> = {\n login: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },\n twoFactorVerify: { windowMs: 15 * 60 * 1000, maxAttempts: 20 },\n twoFactorResend: { windowMs: 15 * 60 * 1000, maxAttempts: 5 },\n register: { windowMs: 60 * 60 * 1000, maxAttempts: 5 },\n passwordResetEmail: { windowMs: 60 * 60 * 1000, maxAttempts: 5 },\n passwordResetConfirm: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },\n getUserByToken: { windowMs: 5 * 60 * 1000, maxAttempts: 30 },\n adminBootstrap: { windowMs: 15 * 60 * 1000, maxAttempts: 10 },\n};\n\nconst RATE_LIMIT_BUCKETS = new Map<string, RateLimitBucket>();\nconst MAX_TRACKED_BUCKETS = 20000;\nconst MAX_WINDOW_MS = Math.max(\n ...Object.values(DEFAULT_PUBLIC_AUTH_RATE_LIMITS).map((rule) => rule.windowMs),\n);\n\nfunction getRule(\n options: AccessiOptions,\n scope: PublicAuthRateLimitScope,\n): PublicAuthRateLimitRuleOptions {\n const configuredRule = options.publicAuthRateLimit?.[scope];\n\n if (\n configuredRule &&\n Number.isFinite(configuredRule.windowMs) &&\n configuredRule.windowMs > 0 &&\n Number.isFinite(configuredRule.maxAttempts) &&\n configuredRule.maxAttempts > 0\n ) {\n return {\n windowMs: Math.trunc(configuredRule.windowMs),\n maxAttempts: Math.trunc(configuredRule.maxAttempts),\n };\n }\n\n return DEFAULT_PUBLIC_AUTH_RATE_LIMITS[scope];\n}\n\nfunction normalizeIdentifier(identifier: string): string | null {\n if (typeof identifier !== 'string') {\n return null;\n }\n\n const normalized = identifier.trim().toLowerCase();\n return normalized === '' ? null : normalized;\n}\n\nfunction pruneRateLimitBuckets(now: number): void {\n if (RATE_LIMIT_BUCKETS.size < MAX_TRACKED_BUCKETS) {\n return;\n }\n\n for (const [key, bucket] of RATE_LIMIT_BUCKETS.entries()) {\n if (bucket.resetAt <= now || bucket.lastSeenAt <= now - MAX_WINDOW_MS * 2) {\n RATE_LIMIT_BUCKETS.delete(key);\n }\n\n if (RATE_LIMIT_BUCKETS.size < MAX_TRACKED_BUCKETS) {\n return;\n }\n }\n}\n\n/** Mantiene il numero di bucket sotto la soglia, scartando i meno recenti. */\nfunction enforceBucketCap(): void {\n while (RATE_LIMIT_BUCKETS.size >= MAX_TRACKED_BUCKETS) {\n let oldestKey: string | undefined;\n let oldestSeen = Number.POSITIVE_INFINITY;\n\n for (const [key, bucket] of RATE_LIMIT_BUCKETS.entries()) {\n if (bucket.lastSeenAt < oldestSeen) {\n oldestSeen = bucket.lastSeenAt;\n oldestKey = key;\n }\n }\n\n if (oldestKey === undefined) {\n return;\n }\n\n RATE_LIMIT_BUCKETS.delete(oldestKey);\n }\n}\n\nfunction consumeBucket(\n bucketKey: string,\n rule: PublicAuthRateLimitRuleOptions,\n now: number,\n): number | null {\n const existingBucket = RATE_LIMIT_BUCKETS.get(bucketKey);\n if (!existingBucket || existingBucket.resetAt <= now) {\n enforceBucketCap();\n RATE_LIMIT_BUCKETS.set(bucketKey, {\n count: 1,\n resetAt: now + rule.windowMs,\n lastSeenAt: now,\n });\n return null;\n }\n\n existingBucket.lastSeenAt = now;\n if (existingBucket.count >= rule.maxAttempts) {\n return Math.max(1, Math.ceil((existingBucket.resetAt - now) / 1000));\n }\n\n existingBucket.count += 1;\n return null;\n}\n\nexport function checkPublicAuthRateLimit(\n options: AccessiOptions,\n scope: PublicAuthRateLimitScope,\n req: Request,\n identifiers: string[] = [],\n): RateLimitDecision {\n if (options.publicAuthRateLimit?.enabled === false) {\n return { allowed: true, retryAfterSeconds: 0 };\n }\n\n const rule = getRule(options, scope);\n const now = Date.now();\n pruneRateLimitBuckets(now);\n\n const ipAddress =\n normalizeIdentifier(req.ip ?? req.socket?.remoteAddress ?? 'unknown') ?? 'unknown';\n\n // Consuma prima il bucket per IP: se gia bloccato non crea bucket per soggetto,\n // evitando crescita di memoria con identificatori sempre diversi.\n const ipRetryAfterSeconds = consumeBucket(`${scope}|ip|${ipAddress}`, rule, now);\n if (ipRetryAfterSeconds) {\n return { allowed: false, retryAfterSeconds: ipRetryAfterSeconds };\n }\n\n let retryAfterSeconds = 0;\n for (const identifier of identifiers) {\n const normalizedIdentifier = normalizeIdentifier(identifier);\n if (!normalizedIdentifier) {\n continue;\n }\n\n const bucketRetryAfterSeconds = consumeBucket(\n `${scope}|ip-subject|${ipAddress}|${normalizedIdentifier}`,\n rule,\n now,\n );\n if (bucketRetryAfterSeconds) {\n retryAfterSeconds = Math.max(retryAfterSeconds, bucketRetryAfterSeconds);\n }\n }\n\n if (retryAfterSeconds > 0) {\n return {\n allowed: false,\n retryAfterSeconds,\n };\n }\n\n return { allowed: true, retryAfterSeconds: 0 };\n}\n\nexport function sendPublicAuthRateLimitExceeded(\n res: Response,\n retryAfterSeconds: number,\n): Response {\n res.setHeader('Retry-After', String(retryAfterSeconds));\n return RestUtilities.sendErrorMessage(\n res,\n 'Troppi tentativi. Riprova piu tardi.',\n 'PublicAuthRateLimit',\n HttpStatus.TOO_MANY_REQUESTS,\n );\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { AccessiOptions } from './AccessiModule';
2
+ /**
3
+ * Logga gli indirizzi utili del modulo Accessi appena il bootstrap e completato, cosi che
4
+ * console, swagger e API siano visibili insieme nei log di avvio.
5
+ */
6
+ export declare function logAccessiStartup(options: AccessiOptions): void;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logAccessiStartup = logAccessiStartup;
4
+ const Logger_1 = require("../Logger");
5
+ const adminBootstrap_1 = require("./security/adminBootstrap");
6
+ /**
7
+ * Logga gli indirizzi utili del modulo Accessi appena il bootstrap e completato, cosi che
8
+ * console, swagger e API siano visibili insieme nei log di avvio.
9
+ */
10
+ function logAccessiStartup(options) {
11
+ var _a, _b;
12
+ const logger = new Logger_1.Logger('Accessi');
13
+ const base = ((_a = options.publicBaseUrl) !== null && _a !== void 0 ? _a : '').replace(/\/+$/, '');
14
+ const url = (path) => `${base}${path}`;
15
+ logger.info('Accessi pronto. Indirizzi utili:');
16
+ logger.info(` Console: ${url('/api/accessi/console')}`);
17
+ logger.info(` Swagger UI: ${url('/accessi/swagger')}`);
18
+ logger.info(` Swagger JSON: ${url('/accessi/swagger.json')}`);
19
+ logger.info(` API: ${url('/api/accessi')}`);
20
+ if (((_b = options.adminBootstrap) === null || _b === void 0 ? void 0 : _b.enabled) === true) {
21
+ const token = (0, adminBootstrap_1.getOrCreateAdminBootstrapToken)();
22
+ logger.warning(` Bootstrap admin abilitato. Token di avvio (monouso, valido fino al riavvio): ${token}`);
23
+ }
24
+ }
25
+ //# sourceMappingURL=startupLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startupLog.js","sourceRoot":"","sources":["../../src/accessi-module/startupLog.ts"],"names":[],"mappings":";;AAQA,8CAeC;AAvBD,sCAAmC;AAEnC,8DAA2E;AAE3E;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,OAAuB;;IACvD,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,CAAC,MAAA,OAAO,CAAC,aAAa,mCAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;IAE/C,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAChD,MAAM,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC;IAC9D,MAAM,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC;IAC/D,MAAM,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IAEtD,IAAI,CAAA,MAAA,OAAO,CAAC,cAAc,0CAAE,OAAO,MAAK,IAAI,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAA,+CAA8B,GAAE,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,kFAAkF,KAAK,EAAE,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC","sourcesContent":["import { Logger } from '../Logger';\nimport type { AccessiOptions } from './AccessiModule';\nimport { getOrCreateAdminBootstrapToken } from './security/adminBootstrap';\n\n/**\n * Logga gli indirizzi utili del modulo Accessi appena il bootstrap e completato, cosi che\n * console, swagger e API siano visibili insieme nei log di avvio.\n */\nexport function logAccessiStartup(options: AccessiOptions): void {\n const logger = new Logger('Accessi');\n const base = (options.publicBaseUrl ?? '').replace(/\\/+$/, '');\n const url = (path: string) => `${base}${path}`;\n\n logger.info('Accessi pronto. Indirizzi utili:');\n logger.info(` Console: ${url('/api/accessi/console')}`);\n logger.info(` Swagger UI: ${url('/accessi/swagger')}`);\n logger.info(` Swagger JSON: ${url('/accessi/swagger.json')}`);\n logger.info(` API: ${url('/api/accessi')}`);\n\n if (options.adminBootstrap?.enabled === true) {\n const token = getOrCreateAdminBootstrapToken();\n logger.warning(` Bootstrap admin abilitato. Token di avvio (monouso, valido fino al riavvio): ${token}`);\n }\n}\n"]}
@@ -21,6 +21,7 @@ const Logger_1 = require("./Logger");
21
21
  const EmilsoftwareModule_1 = require("./EmilsoftwareModule");
22
22
  const authenticateGen_1 = require("./accessi-module/middleware/authenticateGen");
23
23
  const accessiRuntime_1 = require("./accessi-module/accessiRuntime");
24
+ const startupLog_1 = require("./accessi-module/startupLog");
24
25
  const accessiValidation_1 = require("./accessi-module/security/accessiValidation");
25
26
  const AccessiHttpExceptionFilter_1 = require("./accessi-module/security/AccessiHttpExceptionFilter");
26
27
  /**
@@ -69,6 +70,7 @@ function initEmilsoftwareModule(app, options) {
69
70
  const accessiServices = (0, accessiRuntime_1.registerAccessiModuleServices)(app, nestApp);
70
71
  app.locals.accessiAuthService = accessiServices.authenticateGenService;
71
72
  (0, authenticateGen_1.setAccessiAuthService)(accessiServices.authenticateGenService);
73
+ (0, startupLog_1.logAccessiStartup)(options.accessiOptions);
72
74
  }
73
75
  logger.info("Emilsoftware module initialized successfully");
74
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"initEmilsoftwareModule.js","sourceRoot":"","sources":["../src/initEmilsoftwareModule.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAoBA,wDA2DC;AA/ED,sDAAwD;AACxD,uCAA2C;AAC3C,+DAA0D;AAC1D,2DAAuD;AACvD,qCAAkC;AAClC,6DAA+E;AAC/E,iFAIqD;AACrD,oEAAgF;AAChF,mFAA0F;AAC1F,qGAAkG;AAElG;;;;GAIG;AACH,SAAsB,sBAAsB,CAAC,GAAgB,EAAE,OAA4B;;QACvF,MAAM,MAAM,GAAW,IAAI,eAAM,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC/D,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc,EAAE,CAAC;YAC1B,IAAA,gDAA8B,GAAE,CAAC;QACrC,CAAC;QAED,IAAI,CAAC;YACD,gFAAgF;YAChF,wEAAwE;YACxE,MAAM,WAAW,GAAG,IAAA,iBAAO,GAAE,CAAC;YAC9B,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CACjC,IAAI,KAAK,MAAM;gBACf,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBACxB,IAAI,KAAK,UAAU;gBACnB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;gBAC5B,IAAI,KAAK,eAAe,CAAC;YAE7B,GAAG,CAAC,GAAG,CAAC,CAAC,GAAY,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;gBAChC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBACzB,OAAO,IAAI,EAAE,CAAC;gBAClB,CAAC;gBACD,OAAO,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACvC,CAAC,CAAC,CAAC;YAEH,MAAM,mBAAmB,GAAG,IAAI,iCAAc,CAAC,WAAW,CAAC,CAAC;YAC5D,MAAM,OAAO,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,uCAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,mBAAmB,EAAE;gBAC/F,UAAU,EAAE,IAAI;aACnB,CAAC,CAAC;YAEH,OAAO,CAAC,UAAU,EAAE,CAAC;YACrB,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc,EAAE,CAAC;gBAC1B,iFAAiF;gBACjF,OAAO,CAAC,cAAc,CAAC,IAAA,+CAA2B,GAAE,CAAC,CAAC;gBACtD,OAAO,CAAC,gBAAgB,CAAC,IAAI,uDAA0B,EAAE,CAAC,CAAC;YAC/D,CAAC;YAED,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE;gBAC3B,OAAO,EAAE,CAAC,UAAU,EAAE,eAAe,CAAC;aACzC,CAAC,CAAC;YAEH,sDAAsD;YACtD,IAAA,4BAAY,EAAC,OAAO,CAAC,CAAC;YAEtB,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc,EAAE,CAAC;gBAC1B,MAAM,eAAe,GAAG,IAAA,8CAA6B,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBACpE,GAAG,CAAC,MAAM,CAAC,kBAAkB,GAAG,eAAe,CAAC,sBAAsB,CAAC;gBACvE,IAAA,uCAAqB,EAAC,eAAe,CAAC,sBAAsB,CAAC,CAAC;YAClE,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAEhE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc,EAAE,CAAC;gBAC1B,IAAA,+CAA6B,EAAC,KAAK,CAAC,CAAC;YACzC,CAAC;YACD,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;YAC/D,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;CAAA","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport { setupSwagger } from \"./swagger/SwaggerConfig\";\nimport { Logger } from \"./Logger\";\nimport { EmilsoftwareModule, EmilsoftwareOptions } from \"./EmilsoftwareModule\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./accessi-module/middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessi-module/accessiRuntime\";\nimport { createAccessiValidationPipe } from './accessi-module/security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './accessi-module/security/AccessiHttpExceptionFilter';\n\n/**\n * Initializes the unified module that combines Accessi and Allegati functionality\n * @param app Express application instance\n * @param options Configuration options for both modules\n */\nexport async function initEmilsoftwareModule(app: Application, options: EmilsoftwareOptions) {\n const logger: Logger = new Logger(initEmilsoftwareModule.name);\n if (options?.accessiOptions) {\n beginAccessiAuthInitialization();\n }\n\n try {\n // Isolate Nest on a dedicated Express instance and dispatch only module routes.\n // This keeps pre-existing routes/middlewares on the host app untouched.\n const nestHostApp = express();\n const isNestRoute = (path: string) =>\n path === \"/api\" ||\n path.startsWith(\"/api/\") ||\n path === \"/swagger\" ||\n path.startsWith(\"/swagger/\") ||\n path === \"/swagger-json\";\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n return nestHostApp(req, res, next);\n });\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n const nestApp = await NestFactory.create(EmilsoftwareModule.forRoot(options), nestExpressInstance, {\n bufferLogs: true\n });\n\n nestApp.enableCors();\n if (options?.accessiOptions) {\n // Keep the Accessi API contract identical for standalone and unified bootstraps.\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n }\n\n nestApp.setGlobalPrefix('api', {\n exclude: ['/swagger', '/swagger/(.*)']\n });\n\n // Setup unified Swagger documentation for all modules\n setupSwagger(nestApp);\n\n await nestApp.init();\n if (options?.accessiOptions) {\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n }\n\n logger.info(\"Emilsoftware module initialized successfully\");\n\n } catch (error) {\n if (options?.accessiOptions) {\n failAccessiAuthInitialization(error);\n }\n logger.error(\"Error initializing Emilsoftware Module:\", error);\n throw error;\n }\n}\n"]}
1
+ {"version":3,"file":"initEmilsoftwareModule.js","sourceRoot":"","sources":["../src/initEmilsoftwareModule.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Application, Request } from \"express\";\nimport { NestFactory } from \"@nestjs/core\";\nimport { ExpressAdapter } from \"@nestjs/platform-express\";\nimport { setupSwagger } from \"./swagger/SwaggerConfig\";\nimport { Logger } from \"./Logger\";\nimport { EmilsoftwareModule, EmilsoftwareOptions } from \"./EmilsoftwareModule\";\nimport {\n beginAccessiAuthInitialization,\n failAccessiAuthInitialization,\n setAccessiAuthService\n} from \"./accessi-module/middleware/authenticateGen\";\nimport { registerAccessiModuleServices } from \"./accessi-module/accessiRuntime\";\nimport { logAccessiStartup } from \"./accessi-module/startupLog\";\nimport { createAccessiValidationPipe } from './accessi-module/security/accessiValidation';\nimport { AccessiHttpExceptionFilter } from './accessi-module/security/AccessiHttpExceptionFilter';\n\n/**\n * Initializes the unified module that combines Accessi and Allegati functionality\n * @param app Express application instance\n * @param options Configuration options for both modules\n */\nexport async function initEmilsoftwareModule(app: Application, options: EmilsoftwareOptions) {\n const logger: Logger = new Logger(initEmilsoftwareModule.name);\n if (options?.accessiOptions) {\n beginAccessiAuthInitialization();\n }\n\n try {\n // Isolate Nest on a dedicated Express instance and dispatch only module routes.\n // This keeps pre-existing routes/middlewares on the host app untouched.\n const nestHostApp = express();\n const isNestRoute = (path: string) =>\n path === \"/api\" ||\n path.startsWith(\"/api/\") ||\n path === \"/swagger\" ||\n path.startsWith(\"/swagger/\") ||\n path === \"/swagger-json\";\n\n app.use((req: Request, res, next) => {\n if (!isNestRoute(req.path)) {\n return next();\n }\n return nestHostApp(req, res, next);\n });\n\n const nestExpressInstance = new ExpressAdapter(nestHostApp);\n const nestApp = await NestFactory.create(EmilsoftwareModule.forRoot(options), nestExpressInstance, {\n bufferLogs: true\n });\n\n nestApp.enableCors();\n if (options?.accessiOptions) {\n // Keep the Accessi API contract identical for standalone and unified bootstraps.\n nestApp.useGlobalPipes(createAccessiValidationPipe());\n nestApp.useGlobalFilters(new AccessiHttpExceptionFilter());\n }\n\n nestApp.setGlobalPrefix('api', {\n exclude: ['/swagger', '/swagger/(.*)']\n });\n\n // Setup unified Swagger documentation for all modules\n setupSwagger(nestApp);\n\n await nestApp.init();\n if (options?.accessiOptions) {\n const accessiServices = registerAccessiModuleServices(app, nestApp);\n app.locals.accessiAuthService = accessiServices.authenticateGenService;\n setAccessiAuthService(accessiServices.authenticateGenService);\n logAccessiStartup(options.accessiOptions);\n }\n\n logger.info(\"Emilsoftware module initialized successfully\");\n\n } catch (error) {\n if (options?.accessiOptions) {\n failAccessiAuthInitialization(error);\n }\n logger.error(\"Error initializing Emilsoftware Module:\", error);\n throw error;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emilsoftware-utilities",
3
- "version": "2.0.0-dev.36",
3
+ "version": "2.0.0-dev.38",
4
4
  "description": "Utilities for EmilSoftware",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",