@saasicat/cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +141 -0
- package/bin/saas-platform.js +185 -0
- package/dist/index.cjs +2284 -0
- package/dist/index.d.cts +548 -0
- package/dist/index.d.ts +548 -0
- package/dist/index.js +2212 -0
- package/package.json +64 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
AUDIT_QUERY_PORT_TOKEN: () => AUDIT_QUERY_PORT_TOKEN,
|
|
35
|
+
AdminCommands: () => AdminCommands,
|
|
36
|
+
AdminManifestDoctorCheck: () => AdminManifestDoctorCheck,
|
|
37
|
+
AdminMfaSetupCommand: () => AdminMfaSetupCommand,
|
|
38
|
+
AdminWhoamiCommand: () => AdminWhoamiCommand,
|
|
39
|
+
AuditCommands: () => AuditCommands,
|
|
40
|
+
AuditTailCommand: () => AuditTailCommand,
|
|
41
|
+
AuditTailFlow: () => AuditTailFlow,
|
|
42
|
+
CLI_CONTEXT_CONFIG_TOKEN: () => CLI_CONTEXT_CONFIG_TOKEN,
|
|
43
|
+
CliContextModule: () => CliContextModule,
|
|
44
|
+
CliContextService: () => CliContextService,
|
|
45
|
+
CliError: () => CliError,
|
|
46
|
+
DEFAULT_MANIFEST_CHECKS: () => DEFAULT_MANIFEST_CHECKS,
|
|
47
|
+
DOCTOR_CHECKS_TOKEN: () => DOCTOR_CHECKS_TOKEN,
|
|
48
|
+
DiscoveryCommands: () => DiscoveryCommands,
|
|
49
|
+
DiscoveryScanCommand: () => DiscoveryScanCommand,
|
|
50
|
+
DiscoverySnapshotDoctorCheck: () => DiscoverySnapshotDoctorCheck,
|
|
51
|
+
DoctorCommands: () => DoctorCommands,
|
|
52
|
+
DoctorFlow: () => DoctorFlow,
|
|
53
|
+
MANIFEST_ACCESS_PORT_TOKEN: () => MANIFEST_ACCESS_PORT_TOKEN,
|
|
54
|
+
MANIFEST_CHECKS_TOKEN: () => MANIFEST_CHECKS_TOKEN,
|
|
55
|
+
ManifestCheckCommand: () => ManifestCheckCommand,
|
|
56
|
+
ManifestCliFlow: () => ManifestCliFlow,
|
|
57
|
+
ManifestCommands: () => ManifestCommands,
|
|
58
|
+
ManifestDumpCommand: () => ManifestDumpCommand,
|
|
59
|
+
ManifestHashCommand: () => ManifestHashCommand,
|
|
60
|
+
ManifestValidateCommand: () => ManifestValidateCommand,
|
|
61
|
+
MfaSetupFlow: () => MfaSetupFlow,
|
|
62
|
+
PLATFORM_DOCTOR_CHECK_PROVIDERS: () => PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
63
|
+
PlanCatalogDoctorCheck: () => PlanCatalogDoctorCheck,
|
|
64
|
+
USER_MANAGEMENT_PORT_TOKEN: () => USER_MANAGEMENT_PORT_TOKEN,
|
|
65
|
+
USER_PORT_TOKEN: () => USER_PORT_TOKEN,
|
|
66
|
+
UserCommands: () => UserCommands,
|
|
67
|
+
UserPortDoctorCheck: () => UserPortDoctorCheck,
|
|
68
|
+
WhoAmIFlow: () => WhoAmIFlow,
|
|
69
|
+
applyFragmentBlocks: () => applyFragmentBlocks,
|
|
70
|
+
extractModelBlocks: () => extractModelBlocks,
|
|
71
|
+
extractModelNames: () => extractModelNames
|
|
72
|
+
});
|
|
73
|
+
module.exports = __toCommonJS(index_exports);
|
|
74
|
+
|
|
75
|
+
// src/tokens.ts
|
|
76
|
+
var CLI_CONTEXT_CONFIG_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/Config");
|
|
77
|
+
var USER_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/UserPort");
|
|
78
|
+
var USER_MANAGEMENT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/UserManagementPort");
|
|
79
|
+
var AUDIT_QUERY_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/AuditQueryPort");
|
|
80
|
+
var DOCTOR_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/DoctorChecks");
|
|
81
|
+
var MANIFEST_ACCESS_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/ManifestAccessPort");
|
|
82
|
+
var MANIFEST_CHECKS_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform-cli/ManifestChecks");
|
|
83
|
+
|
|
84
|
+
// src/cli-context.service.ts
|
|
85
|
+
var os = __toESM(require("os"), 1);
|
|
86
|
+
var readline = __toESM(require("readline"), 1);
|
|
87
|
+
var import_common = require("@nestjs/common");
|
|
88
|
+
var import_nest = require("@saasicat/nest");
|
|
89
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
90
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
91
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
92
|
+
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;
|
|
93
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
94
|
+
}
|
|
95
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
96
|
+
function _ts_metadata(k, v) {
|
|
97
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
98
|
+
}
|
|
99
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
100
|
+
function _ts_param(paramIndex, decorator) {
|
|
101
|
+
return function(target, key) {
|
|
102
|
+
decorator(target, key, paramIndex);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
__name(_ts_param, "_ts_param");
|
|
106
|
+
var CliContextService = class {
|
|
107
|
+
static {
|
|
108
|
+
__name(this, "CliContextService");
|
|
109
|
+
}
|
|
110
|
+
config;
|
|
111
|
+
users;
|
|
112
|
+
mfa;
|
|
113
|
+
audit;
|
|
114
|
+
constructor(config, users, mfa, audit) {
|
|
115
|
+
this.config = config;
|
|
116
|
+
this.users = users;
|
|
117
|
+
this.mfa = mfa;
|
|
118
|
+
this.audit = audit;
|
|
119
|
+
}
|
|
120
|
+
// ---------------------------------------------------------------------
|
|
121
|
+
// §1 Identity
|
|
122
|
+
// ---------------------------------------------------------------------
|
|
123
|
+
resolveIdentity(asFlag) {
|
|
124
|
+
const fromEnv = process.env[this.config.adminEmailEnvVar] ?? "";
|
|
125
|
+
const email = (asFlag ?? fromEnv).trim().toLowerCase();
|
|
126
|
+
if (!email) {
|
|
127
|
+
throw new CliError("NO_IDENTITY", `Keine Admin-Identit\xE4t gesetzt. Bitte $${this.config.adminEmailEnvVar} setzen oder --as <email> \xFCbergeben.`, 2);
|
|
128
|
+
}
|
|
129
|
+
const host = os.hostname();
|
|
130
|
+
return {
|
|
131
|
+
email,
|
|
132
|
+
host,
|
|
133
|
+
actor: `cli:${email}:${host}`
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Loads + validates the user for a CLI identity:
|
|
138
|
+
* - user exists + active
|
|
139
|
+
* - user has platform role SUPER_ADMIN
|
|
140
|
+
*
|
|
141
|
+
* Throws `CliError(NOT_SUPER_ADMIN, exit=2)` otherwise.
|
|
142
|
+
*/
|
|
143
|
+
async ensureSuperAdmin(identity) {
|
|
144
|
+
const user = await this.users.findByEmail(identity.email);
|
|
145
|
+
if (!user || user.deletedAt || !user.isActive) {
|
|
146
|
+
throw new CliError("USER_NOT_FOUND", `SUPER_ADMIN-User ${identity.email} nicht gefunden oder inaktiv.`, 2);
|
|
147
|
+
}
|
|
148
|
+
if (user.platformRole !== "SUPER_ADMIN") {
|
|
149
|
+
throw new CliError("NOT_SUPER_ADMIN", `User ${identity.email} hat Rolle ${user.platformRole} \u2014 nur SUPER_ADMIN darf das CLI nutzen.`, 2);
|
|
150
|
+
}
|
|
151
|
+
return user;
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------
|
|
154
|
+
// §2 MFA — mandatory TOTP
|
|
155
|
+
// ---------------------------------------------------------------------
|
|
156
|
+
/**
|
|
157
|
+
* Prompts the user for a TOTP code and verifies it against the stored
|
|
158
|
+
* secret (via the platform `MfaService` → `MfaPort`).
|
|
159
|
+
*
|
|
160
|
+
* Bypass: `process.env[mfaSkipEnvVar] === '1'` AND `!isProductionEnvironment()`.
|
|
161
|
+
*/
|
|
162
|
+
async requireMfa(userId) {
|
|
163
|
+
if (process.env[this.config.mfaSkipEnvVar] === "1" && !this.config.isProductionEnvironment()) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const enabled = await this.mfa.isEnabled(userId);
|
|
167
|
+
if (!enabled) {
|
|
168
|
+
throw new CliError("MFA_NOT_SET_UP", "MFA ist nicht konfiguriert. Bitte zuerst 'admin mfa-setup' ausf\xFChren.", 3);
|
|
169
|
+
}
|
|
170
|
+
const code = await this.prompt("TOTP-Code: ");
|
|
171
|
+
const ok2 = await this.mfa.verify({
|
|
172
|
+
userId,
|
|
173
|
+
code
|
|
174
|
+
});
|
|
175
|
+
if (!ok2) {
|
|
176
|
+
throw new CliError("MFA_FAILED", "TOTP-Code ung\xFCltig.", 3);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// ---------------------------------------------------------------------
|
|
180
|
+
// §3 Production confirmation
|
|
181
|
+
// ---------------------------------------------------------------------
|
|
182
|
+
async ensureProductionConfirmation(opts = {}) {
|
|
183
|
+
if (!this.config.isProductionEnvironment()) return;
|
|
184
|
+
if (opts.yes) return;
|
|
185
|
+
const answer = await this.prompt("Tippe production zur Best\xE4tigung: ");
|
|
186
|
+
if (answer.trim().toLowerCase() !== "production") {
|
|
187
|
+
throw new CliError("PRODUCTION_CONFIRM_ABORTED", "Production-Confirmation abgebrochen.", 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// ---------------------------------------------------------------------
|
|
191
|
+
// §5 Audit
|
|
192
|
+
// ---------------------------------------------------------------------
|
|
193
|
+
/**
|
|
194
|
+
* Writes an audit-log entry with the CLI actor tag. Wrapper over
|
|
195
|
+
* `AdminAuditService.log` with automatic `fromCli` construction.
|
|
196
|
+
*/
|
|
197
|
+
async log(input) {
|
|
198
|
+
const actor = this.audit.fromCli({
|
|
199
|
+
id: input.userId,
|
|
200
|
+
email: input.identity.email
|
|
201
|
+
});
|
|
202
|
+
await this.audit.log({
|
|
203
|
+
actor,
|
|
204
|
+
entity: input.entity,
|
|
205
|
+
entityId: input.entityId,
|
|
206
|
+
action: input.action,
|
|
207
|
+
changes: input.changes
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// ---------------------------------------------------------------------
|
|
211
|
+
// Output helpers (§4)
|
|
212
|
+
// ---------------------------------------------------------------------
|
|
213
|
+
table(rows) {
|
|
214
|
+
if (rows.length === 0) {
|
|
215
|
+
console.log("\u2014 keine Eintr\xE4ge \u2014");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
console.table(rows);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Reads a line from stdin. Override hook for tests: set
|
|
222
|
+
* `process.env.SAAS_PLATFORM_CLI_PROMPT_REPLY` to a reply, then
|
|
223
|
+
* the method succeeds without interaction.
|
|
224
|
+
*/
|
|
225
|
+
async prompt(question) {
|
|
226
|
+
const testReply = process.env.SAAS_PLATFORM_CLI_PROMPT_REPLY;
|
|
227
|
+
if (testReply !== void 0) return testReply;
|
|
228
|
+
const rl = readline.createInterface({
|
|
229
|
+
input: process.stdin,
|
|
230
|
+
output: process.stdout
|
|
231
|
+
});
|
|
232
|
+
return new Promise((resolve) => {
|
|
233
|
+
rl.question(question, (answer) => {
|
|
234
|
+
rl.close();
|
|
235
|
+
resolve(answer);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
CliContextService = _ts_decorate([
|
|
241
|
+
(0, import_common.Injectable)(),
|
|
242
|
+
_ts_param(0, (0, import_common.Inject)(CLI_CONTEXT_CONFIG_TOKEN)),
|
|
243
|
+
_ts_param(1, (0, import_common.Inject)(USER_PORT_TOKEN)),
|
|
244
|
+
_ts_metadata("design:type", Function),
|
|
245
|
+
_ts_metadata("design:paramtypes", [
|
|
246
|
+
typeof CliContextConfig === "undefined" ? Object : CliContextConfig,
|
|
247
|
+
typeof UserPort === "undefined" ? Object : UserPort,
|
|
248
|
+
typeof import_nest.MfaService === "undefined" ? Object : import_nest.MfaService,
|
|
249
|
+
typeof import_nest.AdminAuditService === "undefined" ? Object : import_nest.AdminAuditService
|
|
250
|
+
])
|
|
251
|
+
], CliContextService);
|
|
252
|
+
var CliError = class extends Error {
|
|
253
|
+
static {
|
|
254
|
+
__name(this, "CliError");
|
|
255
|
+
}
|
|
256
|
+
code;
|
|
257
|
+
exitCode;
|
|
258
|
+
constructor(code, message, exitCode) {
|
|
259
|
+
super(message), this.code = code, this.exitCode = exitCode;
|
|
260
|
+
this.name = "CliError";
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// src/mfa-setup-flow.ts
|
|
265
|
+
var import_common2 = require("@nestjs/common");
|
|
266
|
+
var import_nest2 = require("@saasicat/nest");
|
|
267
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
268
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
269
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
270
|
+
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;
|
|
271
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
272
|
+
}
|
|
273
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
274
|
+
function _ts_metadata2(k, v) {
|
|
275
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
276
|
+
}
|
|
277
|
+
__name(_ts_metadata2, "_ts_metadata");
|
|
278
|
+
var MfaSetupFlow = class {
|
|
279
|
+
static {
|
|
280
|
+
__name(this, "MfaSetupFlow");
|
|
281
|
+
}
|
|
282
|
+
ctx;
|
|
283
|
+
mfa;
|
|
284
|
+
constructor(ctx, mfa) {
|
|
285
|
+
this.ctx = ctx;
|
|
286
|
+
this.mfa = mfa;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Runs the full setup flow. Throws `CliError` subclasses on
|
|
290
|
+
* auth/identity/confirmation errors.
|
|
291
|
+
*/
|
|
292
|
+
async run(options) {
|
|
293
|
+
const identity = this.ctx.resolveIdentity(options.asFlag);
|
|
294
|
+
const user = await this.ctx.ensureSuperAdmin(identity);
|
|
295
|
+
const alreadyEnabled = await this.mfa.isEnabled(user.id);
|
|
296
|
+
if (alreadyEnabled && !options.force) {
|
|
297
|
+
const answer = await this.ctx.prompt("MFA ist bereits konfiguriert. Tippe `yes`, um das Secret zu \xFCberschreiben: ");
|
|
298
|
+
if (answer.trim().toLowerCase() !== "yes") {
|
|
299
|
+
throw new CliError("MFA_SETUP_ABORTED", "Re-Setup nicht best\xE4tigt \u2014 bestehendes Secret bleibt aktiv.", 1);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const setup = await this.mfa.setup(user.id, user.email, options.issuer);
|
|
303
|
+
await this.ctx.log({
|
|
304
|
+
identity,
|
|
305
|
+
userId: user.id,
|
|
306
|
+
entity: "User",
|
|
307
|
+
entityId: user.id,
|
|
308
|
+
action: alreadyEnabled ? "MFA_SETUP_RESET" : "MFA_SETUP_COMPLETED",
|
|
309
|
+
changes: {
|
|
310
|
+
issuer: options.issuer
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
return {
|
|
314
|
+
secret: setup.secret,
|
|
315
|
+
otpauthUri: setup.otpauthUri,
|
|
316
|
+
userId: user.id,
|
|
317
|
+
userEmail: user.email
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Helper: returns a human-readable output block with secret + otpauthUri
|
|
322
|
+
* for `console.log` in the consumer command. Consumers may prepend their
|
|
323
|
+
* own QR-code renderer (e.g. `qrcode-terminal`).
|
|
324
|
+
*/
|
|
325
|
+
formatSetupResult(result) {
|
|
326
|
+
return [
|
|
327
|
+
`MFA-Setup f\xFCr ${result.userEmail} abgeschlossen.`,
|
|
328
|
+
"",
|
|
329
|
+
`Secret (Base32): ${result.secret}`,
|
|
330
|
+
`otpauth-URI: ${result.otpauthUri}`,
|
|
331
|
+
"",
|
|
332
|
+
"Bitte den otpauth-URI in den Authenticator (Google Authenticator,",
|
|
333
|
+
"1Password, \u2026) importieren oder als QR-Code in einem QR-Generator",
|
|
334
|
+
"rendern. Danach mit dem ersten TOTP-Code testen, dass der Login",
|
|
335
|
+
"funktioniert \u2014 sonst kommst du nicht mehr ans CLI."
|
|
336
|
+
].join("\n");
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
MfaSetupFlow = _ts_decorate2([
|
|
340
|
+
(0, import_common2.Injectable)(),
|
|
341
|
+
_ts_metadata2("design:type", Function),
|
|
342
|
+
_ts_metadata2("design:paramtypes", [
|
|
343
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
344
|
+
typeof import_nest2.MfaService === "undefined" ? Object : import_nest2.MfaService
|
|
345
|
+
])
|
|
346
|
+
], MfaSetupFlow);
|
|
347
|
+
|
|
348
|
+
// src/whoami-flow.ts
|
|
349
|
+
var import_common3 = require("@nestjs/common");
|
|
350
|
+
var import_nest3 = require("@saasicat/nest");
|
|
351
|
+
function _ts_decorate3(decorators, target, key, desc) {
|
|
352
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
353
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
354
|
+
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;
|
|
355
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
356
|
+
}
|
|
357
|
+
__name(_ts_decorate3, "_ts_decorate");
|
|
358
|
+
function _ts_metadata3(k, v) {
|
|
359
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
360
|
+
}
|
|
361
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
362
|
+
function _ts_param2(paramIndex, decorator) {
|
|
363
|
+
return function(target, key) {
|
|
364
|
+
decorator(target, key, paramIndex);
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
__name(_ts_param2, "_ts_param");
|
|
368
|
+
var WhoAmIFlow = class {
|
|
369
|
+
static {
|
|
370
|
+
__name(this, "WhoAmIFlow");
|
|
371
|
+
}
|
|
372
|
+
config;
|
|
373
|
+
ctx;
|
|
374
|
+
mfa;
|
|
375
|
+
constructor(config, ctx, mfa) {
|
|
376
|
+
this.config = config;
|
|
377
|
+
this.ctx = ctx;
|
|
378
|
+
this.mfa = mfa;
|
|
379
|
+
}
|
|
380
|
+
async run(asFlag) {
|
|
381
|
+
const identity = this.ctx.resolveIdentity(asFlag);
|
|
382
|
+
const isProduction = this.config.isProductionEnvironment();
|
|
383
|
+
const mfaSkipActive = !isProduction && process.env[this.config.mfaSkipEnvVar] === "1";
|
|
384
|
+
let userId = null;
|
|
385
|
+
let isSuperAdmin = false;
|
|
386
|
+
let mfaEnabled = false;
|
|
387
|
+
try {
|
|
388
|
+
const user = await this.ctx.ensureSuperAdmin(identity);
|
|
389
|
+
userId = user.id;
|
|
390
|
+
isSuperAdmin = true;
|
|
391
|
+
mfaEnabled = await this.mfa.isEnabled(user.id);
|
|
392
|
+
} catch {
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
email: identity.email,
|
|
396
|
+
host: identity.host,
|
|
397
|
+
actor: identity.actor,
|
|
398
|
+
userId,
|
|
399
|
+
isSuperAdmin,
|
|
400
|
+
mfaEnabled,
|
|
401
|
+
isProduction,
|
|
402
|
+
mfaSkipActive
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
formatResult(r) {
|
|
406
|
+
const lines = [
|
|
407
|
+
`Identit\xE4t: ${r.email}`,
|
|
408
|
+
`Host: ${r.host}`,
|
|
409
|
+
`Actor-Tag: ${r.actor}`,
|
|
410
|
+
`User-ID: ${r.userId ?? "\u2014 (User nicht gefunden)"}`,
|
|
411
|
+
`Plattform-Rolle: ${r.isSuperAdmin ? "SUPER_ADMIN \u2713" : "\u2014 (kein SUPER_ADMIN!)"}`,
|
|
412
|
+
`MFA konfiguriert: ${r.mfaEnabled ? "\u2713" : "\u2717 \u2014 bitte `admin mfa-setup` ausf\xFChren"}`,
|
|
413
|
+
`Environment: ${r.isProduction ? "PRODUCTION" : "non-production"}`
|
|
414
|
+
];
|
|
415
|
+
if (r.mfaSkipActive) {
|
|
416
|
+
lines.push("\u26A0 MFA-Bypass aktiv (SKIP-Env-Var gesetzt, non-prod)");
|
|
417
|
+
}
|
|
418
|
+
return lines.join("\n");
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
WhoAmIFlow = _ts_decorate3([
|
|
422
|
+
(0, import_common3.Injectable)(),
|
|
423
|
+
_ts_param2(0, (0, import_common3.Inject)(CLI_CONTEXT_CONFIG_TOKEN)),
|
|
424
|
+
_ts_metadata3("design:type", Function),
|
|
425
|
+
_ts_metadata3("design:paramtypes", [
|
|
426
|
+
typeof CliContextConfig === "undefined" ? Object : CliContextConfig,
|
|
427
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
428
|
+
typeof import_nest3.MfaService === "undefined" ? Object : import_nest3.MfaService
|
|
429
|
+
])
|
|
430
|
+
], WhoAmIFlow);
|
|
431
|
+
|
|
432
|
+
// src/audit-tail-flow.ts
|
|
433
|
+
var import_common4 = require("@nestjs/common");
|
|
434
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
435
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
436
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
437
|
+
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;
|
|
438
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
439
|
+
}
|
|
440
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
441
|
+
function _ts_metadata4(k, v) {
|
|
442
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
443
|
+
}
|
|
444
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
445
|
+
function _ts_param3(paramIndex, decorator) {
|
|
446
|
+
return function(target, key) {
|
|
447
|
+
decorator(target, key, paramIndex);
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
__name(_ts_param3, "_ts_param");
|
|
451
|
+
var AuditTailFlow = class {
|
|
452
|
+
static {
|
|
453
|
+
__name(this, "AuditTailFlow");
|
|
454
|
+
}
|
|
455
|
+
auditQuery;
|
|
456
|
+
constructor(auditQuery) {
|
|
457
|
+
this.auditQuery = auditQuery;
|
|
458
|
+
}
|
|
459
|
+
async run(options = {}) {
|
|
460
|
+
const filter = {};
|
|
461
|
+
if (options.actor) filter.actorTag = options.actor;
|
|
462
|
+
if (options.action) filter.action = options.action;
|
|
463
|
+
if (options.entity) filter.entity = options.entity;
|
|
464
|
+
if (options.since) filter.from = options.since;
|
|
465
|
+
if (options.limit) filter.pageSize = options.limit;
|
|
466
|
+
return this.auditQuery.list(filter);
|
|
467
|
+
}
|
|
468
|
+
/** Format the result as an ASCII table for `console.table`. */
|
|
469
|
+
formatRows(entries) {
|
|
470
|
+
return entries.map((e) => ({
|
|
471
|
+
createdAt: e.createdAt,
|
|
472
|
+
actor: e.actorTag ?? "\u2014",
|
|
473
|
+
entity: e.entity,
|
|
474
|
+
entityId: this.truncate(e.entityId, 12),
|
|
475
|
+
action: e.action
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
478
|
+
truncate(s, maxLen) {
|
|
479
|
+
return s.length > maxLen ? s.slice(0, maxLen - 1) + "\u2026" : s;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
AuditTailFlow = _ts_decorate4([
|
|
483
|
+
(0, import_common4.Injectable)(),
|
|
484
|
+
_ts_param3(0, (0, import_common4.Inject)(AUDIT_QUERY_PORT_TOKEN)),
|
|
485
|
+
_ts_metadata4("design:type", Function),
|
|
486
|
+
_ts_metadata4("design:paramtypes", [
|
|
487
|
+
typeof AuditQueryPort === "undefined" ? Object : AuditQueryPort
|
|
488
|
+
])
|
|
489
|
+
], AuditTailFlow);
|
|
490
|
+
|
|
491
|
+
// src/doctor-flow.ts
|
|
492
|
+
var import_common5 = require("@nestjs/common");
|
|
493
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
494
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
495
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
496
|
+
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;
|
|
497
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
498
|
+
}
|
|
499
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
500
|
+
function _ts_metadata5(k, v) {
|
|
501
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
502
|
+
}
|
|
503
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
504
|
+
function _ts_param4(paramIndex, decorator) {
|
|
505
|
+
return function(target, key) {
|
|
506
|
+
decorator(target, key, paramIndex);
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
__name(_ts_param4, "_ts_param");
|
|
510
|
+
var DoctorFlow = class {
|
|
511
|
+
static {
|
|
512
|
+
__name(this, "DoctorFlow");
|
|
513
|
+
}
|
|
514
|
+
checks;
|
|
515
|
+
constructor(checks) {
|
|
516
|
+
this.checks = checks;
|
|
517
|
+
}
|
|
518
|
+
async run() {
|
|
519
|
+
const results = [];
|
|
520
|
+
let overall = "ok";
|
|
521
|
+
for (const check of this.checks) {
|
|
522
|
+
try {
|
|
523
|
+
const r = await check.run();
|
|
524
|
+
results.push({
|
|
525
|
+
id: check.id,
|
|
526
|
+
label: check.label,
|
|
527
|
+
...r
|
|
528
|
+
});
|
|
529
|
+
overall = aggregateSeverity(overall, r.severity);
|
|
530
|
+
} catch (err2) {
|
|
531
|
+
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
532
|
+
results.push({
|
|
533
|
+
id: check.id,
|
|
534
|
+
label: check.label,
|
|
535
|
+
severity: "error",
|
|
536
|
+
message: `Check threw an exception: ${message}`
|
|
537
|
+
});
|
|
538
|
+
overall = "error";
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
overall,
|
|
543
|
+
checks: results
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
/** Returns the appropriate CLI exit code: 0 for `ok`/`warning`, 4 for `error`. */
|
|
547
|
+
exitCodeFor(report) {
|
|
548
|
+
return report.overall === "error" ? 4 : 0;
|
|
549
|
+
}
|
|
550
|
+
formatReport(report) {
|
|
551
|
+
const lines = [
|
|
552
|
+
`Doctor-Check (Gesamtstatus: ${report.overall.toUpperCase()})`,
|
|
553
|
+
""
|
|
554
|
+
];
|
|
555
|
+
for (const c of report.checks) {
|
|
556
|
+
const icon = c.severity === "ok" ? "\u2713" : c.severity === "warning" ? "\u26A0" : "\u2717";
|
|
557
|
+
lines.push(` ${icon} ${c.label}: ${c.message}`);
|
|
558
|
+
}
|
|
559
|
+
return lines.join("\n");
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
DoctorFlow = _ts_decorate5([
|
|
563
|
+
(0, import_common5.Injectable)(),
|
|
564
|
+
_ts_param4(0, (0, import_common5.Inject)(DOCTOR_CHECKS_TOKEN)),
|
|
565
|
+
_ts_metadata5("design:type", Function),
|
|
566
|
+
_ts_metadata5("design:paramtypes", [
|
|
567
|
+
Array
|
|
568
|
+
])
|
|
569
|
+
], DoctorFlow);
|
|
570
|
+
function aggregateSeverity(a, b) {
|
|
571
|
+
if (a === "error" || b === "error") return "error";
|
|
572
|
+
if (a === "warning" || b === "warning") return "warning";
|
|
573
|
+
return "ok";
|
|
574
|
+
}
|
|
575
|
+
__name(aggregateSeverity, "aggregateSeverity");
|
|
576
|
+
|
|
577
|
+
// src/manifest-checks.ts
|
|
578
|
+
var COMPONENT_KEY_PATTERN = /^[a-z][a-z0-9]*([.-][a-z0-9]+)+$/;
|
|
579
|
+
var ACTION_KEY_PATTERN = /^[A-Z][A-Z0-9_]+$/;
|
|
580
|
+
var TENANT_ACTION_KEY_PATTERN = /^[a-z][a-zA-Z0-9_]*(\.[a-z][a-zA-Z0-9_]*)+$/;
|
|
581
|
+
var CAPABILITY_PATTERN = /^[a-z][a-zA-Z0-9_]*(\.[a-z][a-zA-Z0-9_]*)+$/;
|
|
582
|
+
var ROUTE_PREFIX = "/admin";
|
|
583
|
+
function ok(message) {
|
|
584
|
+
return {
|
|
585
|
+
severity: "ok",
|
|
586
|
+
message
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
__name(ok, "ok");
|
|
590
|
+
function err(message, paths) {
|
|
591
|
+
return {
|
|
592
|
+
severity: "error",
|
|
593
|
+
message,
|
|
594
|
+
paths
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
__name(err, "err");
|
|
598
|
+
function allProjectPages(m) {
|
|
599
|
+
return m.navigation?.projectPages ?? [];
|
|
600
|
+
}
|
|
601
|
+
__name(allProjectPages, "allProjectPages");
|
|
602
|
+
function allKpiCards(m) {
|
|
603
|
+
return m.dashboard?.kpiCards ?? [];
|
|
604
|
+
}
|
|
605
|
+
__name(allKpiCards, "allKpiCards");
|
|
606
|
+
function allTenantActions(m) {
|
|
607
|
+
return m.tenants?.actions ?? [];
|
|
608
|
+
}
|
|
609
|
+
__name(allTenantActions, "allTenantActions");
|
|
610
|
+
function allTenantColumns(m) {
|
|
611
|
+
return m.tenants?.columns ?? [];
|
|
612
|
+
}
|
|
613
|
+
__name(allTenantColumns, "allTenantColumns");
|
|
614
|
+
function allAuditActions(m) {
|
|
615
|
+
return m.audit?.actions ?? [];
|
|
616
|
+
}
|
|
617
|
+
__name(allAuditActions, "allAuditActions");
|
|
618
|
+
function allCapabilities(m) {
|
|
619
|
+
return Object.keys(m.capabilities ?? {});
|
|
620
|
+
}
|
|
621
|
+
__name(allCapabilities, "allCapabilities");
|
|
622
|
+
var DEFAULT_MANIFEST_CHECKS = [
|
|
623
|
+
{
|
|
624
|
+
id: "manifest.schema-version",
|
|
625
|
+
label: "schemaVersion ist 1",
|
|
626
|
+
run: /* @__PURE__ */ __name((m) => m.schemaVersion === 1 ? ok("schemaVersion=1") : err(`Unerwartete schemaVersion ${m.schemaVersion}`), "run")
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
id: "manifest.hash-format",
|
|
630
|
+
label: "manifestHash folgt sha256-<base64url>-Pattern",
|
|
631
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
632
|
+
const hash = m.build?.manifestHash;
|
|
633
|
+
if (!hash) return err("manifestHash fehlt");
|
|
634
|
+
return /^sha256-[A-Za-z0-9_-]+$/.test(hash) ? ok(hash) : err(`manifestHash hat falsches Format: ${hash}`);
|
|
635
|
+
}, "run")
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
id: "manifest.project-page-component-keys",
|
|
639
|
+
label: "ProjectPage.componentKey-Format (lowercase-hyphenated ODER namespace.dot)",
|
|
640
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
641
|
+
const bad = [];
|
|
642
|
+
for (const p of allProjectPages(m)) {
|
|
643
|
+
if (!COMPONENT_KEY_PATTERN.test(p.componentKey)) bad.push(p.componentKey);
|
|
644
|
+
}
|
|
645
|
+
return bad.length === 0 ? ok(`${allProjectPages(m).length} ProjectPage(s) ok`) : err(`${bad.length} componentKey(s) verletzen das Pattern`, bad);
|
|
646
|
+
}, "run")
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
id: "manifest.tenant-action-keys",
|
|
650
|
+
label: "TenantAction.actionKey-Format (domain.action \u2014 SPEC \xA74.2.1)",
|
|
651
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
652
|
+
const bad = [];
|
|
653
|
+
for (const a of allTenantActions(m)) {
|
|
654
|
+
if (!TENANT_ACTION_KEY_PATTERN.test(a.actionKey)) bad.push(a.actionKey);
|
|
655
|
+
}
|
|
656
|
+
return bad.length === 0 ? ok("alle actionKeys folgen domain.action") : err(`${bad.length} actionKey(s) verletzen das Pattern`, bad);
|
|
657
|
+
}, "run")
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
id: "manifest.audit-action-keys",
|
|
661
|
+
label: "AuditAction.key-Format (SCREAMING_SNAKE_CASE)",
|
|
662
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
663
|
+
const bad = [];
|
|
664
|
+
for (const a of allAuditActions(m)) {
|
|
665
|
+
if (!ACTION_KEY_PATTERN.test(a.key)) bad.push(a.key);
|
|
666
|
+
}
|
|
667
|
+
return bad.length === 0 ? ok("alle AuditAction.keys SCREAMING_SNAKE_CASE") : err(`${bad.length} AuditAction.key(s) verletzen das Pattern`, bad);
|
|
668
|
+
}, "run")
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
id: "manifest.capabilities-pattern",
|
|
672
|
+
label: "Capability-Pattern <domain>.<action> (SPEC \xA74.2.1)",
|
|
673
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
674
|
+
const bad = allCapabilities(m).filter((c) => !CAPABILITY_PATTERN.test(c));
|
|
675
|
+
return bad.length === 0 ? ok(`${allCapabilities(m).length} Capabilities ok`) : err(`${bad.length} Capability/-ies verletzen das Pattern`, bad);
|
|
676
|
+
}, "run")
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
id: "manifest.required-capabilities-known",
|
|
680
|
+
label: "requiredCapability-Referenzen existieren in capabilities-Map",
|
|
681
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
682
|
+
const known = new Set(allCapabilities(m));
|
|
683
|
+
const bad = [];
|
|
684
|
+
const visit = /* @__PURE__ */ __name((cap, ctx) => {
|
|
685
|
+
if (cap && !known.has(cap)) bad.push(`${ctx ?? "?"}: ${cap}`);
|
|
686
|
+
}, "visit");
|
|
687
|
+
for (const p of allProjectPages(m)) visit(p.requiredCapability, p.id);
|
|
688
|
+
for (const k of allKpiCards(m)) visit(k.requiredCapability, k.id);
|
|
689
|
+
for (const a of allTenantActions(m)) visit(a.requiredCapability, a.id);
|
|
690
|
+
for (const c of allTenantColumns(m)) visit(c.requiredCapability, c.key);
|
|
691
|
+
return bad.length === 0 ? ok("alle requiredCapability-Refs aufgel\xF6st") : err(`${bad.length} unbekannte Capability-Ref(s)`, bad);
|
|
692
|
+
}, "run")
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
id: "manifest.route-prefix",
|
|
696
|
+
label: "ProjectPage.route beginnt mit /admin",
|
|
697
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
698
|
+
const bad = [];
|
|
699
|
+
for (const p of allProjectPages(m)) {
|
|
700
|
+
if (!p.route.startsWith(ROUTE_PREFIX)) bad.push(p.route);
|
|
701
|
+
}
|
|
702
|
+
return bad.length === 0 ? ok("alle ProjectPage-Routes unter /admin") : err(`${bad.length} Route(s) ohne /admin-Prefix`, bad);
|
|
703
|
+
}, "run")
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
id: "manifest.kpi-slot-priority",
|
|
707
|
+
label: "KpiCard.slotPriority (falls gesetzt) ist endliche Zahl",
|
|
708
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
709
|
+
const bad = [];
|
|
710
|
+
for (const k of allKpiCards(m)) {
|
|
711
|
+
if (k.slotPriority !== void 0 && !Number.isFinite(k.slotPriority)) {
|
|
712
|
+
bad.push(k.id);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return bad.length === 0 ? ok("alle slotPriority-Werte endlich") : err(`${bad.length} KpiCard(s) mit ung\xFCltiger slotPriority`, bad);
|
|
716
|
+
}, "run")
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
id: "manifest.unique-project-page-ids",
|
|
720
|
+
label: "ProjectPage.id ist unique",
|
|
721
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
722
|
+
const seen = /* @__PURE__ */ new Map();
|
|
723
|
+
for (const p of allProjectPages(m)) {
|
|
724
|
+
seen.set(p.id, (seen.get(p.id) ?? 0) + 1);
|
|
725
|
+
}
|
|
726
|
+
const dups = [
|
|
727
|
+
...seen.entries()
|
|
728
|
+
].filter(([, n]) => n > 1).map(([k]) => k);
|
|
729
|
+
return dups.length === 0 ? ok("alle ProjectPage.id eindeutig") : err(`${dups.length} doppelte ProjectPage.id`, dups);
|
|
730
|
+
}, "run")
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
id: "manifest.unique-action-keys",
|
|
734
|
+
label: "actionKeys sind je Namespace (TenantAction / AuditAction) eindeutig",
|
|
735
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
736
|
+
const dups = [];
|
|
737
|
+
const tenantSeen = /* @__PURE__ */ new Map();
|
|
738
|
+
for (const a of allTenantActions(m)) {
|
|
739
|
+
tenantSeen.set(a.actionKey, (tenantSeen.get(a.actionKey) ?? 0) + 1);
|
|
740
|
+
}
|
|
741
|
+
for (const [k, n] of tenantSeen) {
|
|
742
|
+
if (n > 1) dups.push(`TenantAction:${k}`);
|
|
743
|
+
}
|
|
744
|
+
const auditSeen = /* @__PURE__ */ new Map();
|
|
745
|
+
for (const a of allAuditActions(m)) {
|
|
746
|
+
auditSeen.set(a.key, (auditSeen.get(a.key) ?? 0) + 1);
|
|
747
|
+
}
|
|
748
|
+
for (const [k, n] of auditSeen) {
|
|
749
|
+
if (n > 1) dups.push(`AuditAction:${k}`);
|
|
750
|
+
}
|
|
751
|
+
return dups.length === 0 ? ok("alle actionKeys je Namespace eindeutig") : err(`${dups.length} doppelte actionKey(s)`, dups);
|
|
752
|
+
}, "run")
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
id: "manifest.tenant-columns-batchable",
|
|
756
|
+
label: "TenantColumns haben endpoint-Pfad f\xFCr Batch-Fetch",
|
|
757
|
+
run: /* @__PURE__ */ __name((m) => {
|
|
758
|
+
const bad = [];
|
|
759
|
+
for (const c of allTenantColumns(m)) {
|
|
760
|
+
if (!c.endpoint || c.endpoint.trim().length === 0) bad.push(c.key);
|
|
761
|
+
else if (c.endpoint.includes("{slug}") || c.endpoint.includes("{tenantId}")) {
|
|
762
|
+
bad.push(`${c.key} (per-Tenant statt batch)`);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return bad.length === 0 ? ok("alle TenantColumns batch-f\xE4hig") : err(`${bad.length} TenantColumn(s) verletzen Batch-Pflicht`, bad);
|
|
766
|
+
}, "run")
|
|
767
|
+
}
|
|
768
|
+
];
|
|
769
|
+
|
|
770
|
+
// src/manifest-cli-flow.ts
|
|
771
|
+
var import_common6 = require("@nestjs/common");
|
|
772
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
773
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
774
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
775
|
+
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;
|
|
776
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
777
|
+
}
|
|
778
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
779
|
+
function _ts_metadata6(k, v) {
|
|
780
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
781
|
+
}
|
|
782
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
783
|
+
function _ts_param5(paramIndex, decorator) {
|
|
784
|
+
return function(target, key) {
|
|
785
|
+
decorator(target, key, paramIndex);
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
__name(_ts_param5, "_ts_param");
|
|
789
|
+
var ManifestCliFlow = class {
|
|
790
|
+
static {
|
|
791
|
+
__name(this, "ManifestCliFlow");
|
|
792
|
+
}
|
|
793
|
+
access;
|
|
794
|
+
checks;
|
|
795
|
+
constructor(access, checks) {
|
|
796
|
+
this.access = access;
|
|
797
|
+
this.checks = checks;
|
|
798
|
+
}
|
|
799
|
+
/** `<app> manifest dump` — JSON output of the current manifest. */
|
|
800
|
+
dump() {
|
|
801
|
+
return this.access.getManifest();
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* `<app> manifest hash` — canonical manifestHash value. Returns
|
|
805
|
+
* the value from `build.manifestHash`. Consumers can pin this in CI
|
|
806
|
+
* (`expected-hash.txt`) and check for drift.
|
|
807
|
+
*/
|
|
808
|
+
hash() {
|
|
809
|
+
const m = this.access.getManifest();
|
|
810
|
+
const h = m.build?.manifestHash;
|
|
811
|
+
if (!h) throw new Error("manifestHash fehlt im Manifest \u2014 Boot-Zeit-Bug?");
|
|
812
|
+
return h;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* `<app> manifest validate` — returns `true` when all structures
|
|
816
|
+
* are present. Deeper schema validation runs separately via Ajv with
|
|
817
|
+
* `@saasicat/spec/schemas/admin-manifest.schema.json`;
|
|
818
|
+
* this helper provides the quick diagnostic without the Ajv cost.
|
|
819
|
+
*/
|
|
820
|
+
validate() {
|
|
821
|
+
const m = this.access.getManifest();
|
|
822
|
+
if (m.schemaVersion !== 1) {
|
|
823
|
+
return {
|
|
824
|
+
ok: false,
|
|
825
|
+
reason: `Unerwartete schemaVersion ${m.schemaVersion}`
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
if (!m.project?.key) {
|
|
829
|
+
return {
|
|
830
|
+
ok: false,
|
|
831
|
+
reason: "Kein `project.key` im Manifest"
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
if (!m.build?.manifestHash) {
|
|
835
|
+
return {
|
|
836
|
+
ok: false,
|
|
837
|
+
reason: "manifestHash fehlt"
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
return {
|
|
841
|
+
ok: true
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* `<app> manifest diff <expected.json>` — flat diff over the two
|
|
846
|
+
* manifest hashes plus list differences for top-level fields. Returns
|
|
847
|
+
* `null` when the manifests are identical.
|
|
848
|
+
*/
|
|
849
|
+
diff(expected) {
|
|
850
|
+
const current = this.access.getManifest();
|
|
851
|
+
if (current.build?.manifestHash === expected.build?.manifestHash) return null;
|
|
852
|
+
const currentKeys = collectComponentKeys(current);
|
|
853
|
+
const expectedKeys = collectComponentKeys(expected);
|
|
854
|
+
return {
|
|
855
|
+
currentHash: current.build?.manifestHash ?? null,
|
|
856
|
+
expectedHash: expected.build?.manifestHash ?? null,
|
|
857
|
+
componentKeysAdded: [
|
|
858
|
+
...currentKeys
|
|
859
|
+
].filter((k) => !expectedKeys.has(k)).sort(),
|
|
860
|
+
componentKeysRemoved: [
|
|
861
|
+
...expectedKeys
|
|
862
|
+
].filter((k) => !currentKeys.has(k)).sort()
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* `<app> manifest check` — runs all registered manifest checks.
|
|
867
|
+
* Aggregates severity (ok/warning/error) into `overall`. The consumer
|
|
868
|
+
* maps `error` to exit code 7 (drift) per `cli-conventions.md` §6.
|
|
869
|
+
*/
|
|
870
|
+
async runChecks() {
|
|
871
|
+
const manifest = this.access.getManifest();
|
|
872
|
+
const checks = [];
|
|
873
|
+
let overall = "ok";
|
|
874
|
+
for (const check of this.checks) {
|
|
875
|
+
try {
|
|
876
|
+
const r = check.run(manifest);
|
|
877
|
+
checks.push({
|
|
878
|
+
id: check.id,
|
|
879
|
+
label: check.label,
|
|
880
|
+
...r
|
|
881
|
+
});
|
|
882
|
+
overall = aggregate(overall, r.severity);
|
|
883
|
+
} catch (err2) {
|
|
884
|
+
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
885
|
+
checks.push({
|
|
886
|
+
id: check.id,
|
|
887
|
+
label: check.label,
|
|
888
|
+
severity: "error",
|
|
889
|
+
message: `Check warf eine Exception: ${message}`
|
|
890
|
+
});
|
|
891
|
+
overall = "error";
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
overall,
|
|
896
|
+
checks
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
/** Returns the appropriate CLI exit code: 0 for `ok`/`warning`, 7 for `error` (drift). */
|
|
900
|
+
exitCodeFor(report) {
|
|
901
|
+
return report.overall === "error" ? 7 : 0;
|
|
902
|
+
}
|
|
903
|
+
formatReport(report) {
|
|
904
|
+
const lines = [
|
|
905
|
+
`Manifest-Check (Gesamtstatus: ${report.overall.toUpperCase()})`,
|
|
906
|
+
""
|
|
907
|
+
];
|
|
908
|
+
for (const c of report.checks) {
|
|
909
|
+
const icon = c.severity === "ok" ? "\u2713" : c.severity === "warning" ? "\u26A0" : "\u2717";
|
|
910
|
+
lines.push(` ${icon} ${c.label}: ${c.message}`);
|
|
911
|
+
if (c.paths && c.paths.length > 0) {
|
|
912
|
+
for (const p of c.paths) lines.push(` \xB7 ${p}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return lines.join("\n");
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
ManifestCliFlow = _ts_decorate6([
|
|
919
|
+
(0, import_common6.Injectable)(),
|
|
920
|
+
_ts_param5(0, (0, import_common6.Inject)(MANIFEST_ACCESS_PORT_TOKEN)),
|
|
921
|
+
_ts_param5(1, (0, import_common6.Inject)(MANIFEST_CHECKS_TOKEN)),
|
|
922
|
+
_ts_metadata6("design:type", Function),
|
|
923
|
+
_ts_metadata6("design:paramtypes", [
|
|
924
|
+
typeof ManifestAccessPort === "undefined" ? Object : ManifestAccessPort,
|
|
925
|
+
Array
|
|
926
|
+
])
|
|
927
|
+
], ManifestCliFlow);
|
|
928
|
+
function aggregate(a, b) {
|
|
929
|
+
if (a === "error" || b === "error") return "error";
|
|
930
|
+
if (a === "warning" || b === "warning") return "warning";
|
|
931
|
+
return "ok";
|
|
932
|
+
}
|
|
933
|
+
__name(aggregate, "aggregate");
|
|
934
|
+
function collectComponentKeys(m) {
|
|
935
|
+
const keys = /* @__PURE__ */ new Set();
|
|
936
|
+
for (const p of m.navigation?.projectPages ?? []) keys.add(p.componentKey);
|
|
937
|
+
return keys;
|
|
938
|
+
}
|
|
939
|
+
__name(collectComponentKeys, "collectComponentKeys");
|
|
940
|
+
|
|
941
|
+
// src/default-doctor-checks.ts
|
|
942
|
+
var import_common7 = require("@nestjs/common");
|
|
943
|
+
var import_nest4 = require("@saasicat/nest");
|
|
944
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
945
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
946
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
947
|
+
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;
|
|
948
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
949
|
+
}
|
|
950
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
951
|
+
function _ts_metadata7(k, v) {
|
|
952
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
953
|
+
}
|
|
954
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
955
|
+
function _ts_param6(paramIndex, decorator) {
|
|
956
|
+
return function(target, key) {
|
|
957
|
+
decorator(target, key, paramIndex);
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
__name(_ts_param6, "_ts_param");
|
|
961
|
+
var PlanCatalogDoctorCheck = class {
|
|
962
|
+
static {
|
|
963
|
+
__name(this, "PlanCatalogDoctorCheck");
|
|
964
|
+
}
|
|
965
|
+
catalog;
|
|
966
|
+
id = "platform.plan-catalog";
|
|
967
|
+
label = "Plan-Catalog im DI";
|
|
968
|
+
constructor(catalog) {
|
|
969
|
+
this.catalog = catalog;
|
|
970
|
+
}
|
|
971
|
+
async run() {
|
|
972
|
+
const plans = this.catalog?.plans ?? [];
|
|
973
|
+
if (plans.length === 0) {
|
|
974
|
+
return {
|
|
975
|
+
severity: "error",
|
|
976
|
+
message: "PlanCatalog enth\xE4lt keine Pl\xE4ne \u2014 Onboarding-Pricing-Page wird leer."
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
return {
|
|
980
|
+
severity: "ok",
|
|
981
|
+
message: `${plans.length} Plan(s), ${this.catalog.features?.length ?? 0} Feature(s) geladen.`,
|
|
982
|
+
details: {
|
|
983
|
+
projectKey: this.catalog.projectKey,
|
|
984
|
+
planIds: plans.map((p) => p.id)
|
|
985
|
+
}
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
PlanCatalogDoctorCheck = _ts_decorate7([
|
|
990
|
+
(0, import_common7.Injectable)(),
|
|
991
|
+
_ts_param6(0, (0, import_common7.Inject)(import_nest4.PLAN_CATALOG_TOKEN)),
|
|
992
|
+
_ts_metadata7("design:type", Function),
|
|
993
|
+
_ts_metadata7("design:paramtypes", [
|
|
994
|
+
typeof PlanCatalog === "undefined" ? Object : PlanCatalog
|
|
995
|
+
])
|
|
996
|
+
], PlanCatalogDoctorCheck);
|
|
997
|
+
var DiscoverySnapshotDoctorCheck = class {
|
|
998
|
+
static {
|
|
999
|
+
__name(this, "DiscoverySnapshotDoctorCheck");
|
|
1000
|
+
}
|
|
1001
|
+
snapshot;
|
|
1002
|
+
id = "platform.discovery-snapshot";
|
|
1003
|
+
label = "Discovery-Snapshot beim Boot";
|
|
1004
|
+
constructor(snapshot) {
|
|
1005
|
+
this.snapshot = snapshot;
|
|
1006
|
+
}
|
|
1007
|
+
async run() {
|
|
1008
|
+
const caps = this.snapshot?.capabilities ?? [];
|
|
1009
|
+
if (caps.length === 0) {
|
|
1010
|
+
return {
|
|
1011
|
+
severity: "warning",
|
|
1012
|
+
message: "Keine Capabilities entdeckt \u2014 Decorator-tragende Module evtl. nicht in AppModule.imports[]."
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
return {
|
|
1016
|
+
severity: "ok",
|
|
1017
|
+
message: `${caps.length} Capabilities, ${this.snapshot.features?.length ?? 0} Features, ${this.snapshot.quotas?.length ?? 0} Quotas.`
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
DiscoverySnapshotDoctorCheck = _ts_decorate7([
|
|
1022
|
+
(0, import_common7.Injectable)(),
|
|
1023
|
+
_ts_param6(0, (0, import_common7.Inject)(import_nest4.DISCOVERY_SNAPSHOT_TOKEN)),
|
|
1024
|
+
_ts_metadata7("design:type", Function),
|
|
1025
|
+
_ts_metadata7("design:paramtypes", [
|
|
1026
|
+
typeof DiscoverySnapshot === "undefined" ? Object : DiscoverySnapshot
|
|
1027
|
+
])
|
|
1028
|
+
], DiscoverySnapshotDoctorCheck);
|
|
1029
|
+
var UserPortDoctorCheck = class {
|
|
1030
|
+
static {
|
|
1031
|
+
__name(this, "UserPortDoctorCheck");
|
|
1032
|
+
}
|
|
1033
|
+
users;
|
|
1034
|
+
id = "platform.user-port";
|
|
1035
|
+
label = "UserPort.findByEmail erreichbar";
|
|
1036
|
+
constructor(users) {
|
|
1037
|
+
this.users = users;
|
|
1038
|
+
}
|
|
1039
|
+
async run() {
|
|
1040
|
+
try {
|
|
1041
|
+
await this.users.findByEmail("__doctor-check__@invalid.local");
|
|
1042
|
+
return {
|
|
1043
|
+
severity: "ok",
|
|
1044
|
+
message: "UserPort antwortet."
|
|
1045
|
+
};
|
|
1046
|
+
} catch (err2) {
|
|
1047
|
+
return {
|
|
1048
|
+
severity: "error",
|
|
1049
|
+
message: `UserPort wirft: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
UserPortDoctorCheck = _ts_decorate7([
|
|
1055
|
+
(0, import_common7.Injectable)(),
|
|
1056
|
+
_ts_param6(0, (0, import_common7.Inject)(USER_PORT_TOKEN)),
|
|
1057
|
+
_ts_metadata7("design:type", Function),
|
|
1058
|
+
_ts_metadata7("design:paramtypes", [
|
|
1059
|
+
typeof UserPort === "undefined" ? Object : UserPort
|
|
1060
|
+
])
|
|
1061
|
+
], UserPortDoctorCheck);
|
|
1062
|
+
var AdminManifestDoctorCheck = class {
|
|
1063
|
+
static {
|
|
1064
|
+
__name(this, "AdminManifestDoctorCheck");
|
|
1065
|
+
}
|
|
1066
|
+
manifest;
|
|
1067
|
+
id = "platform.admin-manifest";
|
|
1068
|
+
label = "AdminManifestService liefert Manifest";
|
|
1069
|
+
constructor(manifest) {
|
|
1070
|
+
this.manifest = manifest;
|
|
1071
|
+
}
|
|
1072
|
+
async run() {
|
|
1073
|
+
try {
|
|
1074
|
+
const m = this.manifest.getManifest();
|
|
1075
|
+
const pageCount = Object.keys(m.navigation?.standardPages ?? {}).length;
|
|
1076
|
+
return {
|
|
1077
|
+
severity: "ok",
|
|
1078
|
+
message: `Manifest mit ${pageCount} Standard-Pages, Hash ${m.build?.manifestHash?.slice(0, 12) ?? "???"}\u2026`
|
|
1079
|
+
};
|
|
1080
|
+
} catch (err2) {
|
|
1081
|
+
return {
|
|
1082
|
+
severity: "error",
|
|
1083
|
+
message: `Manifest-Build wirft: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
AdminManifestDoctorCheck = _ts_decorate7([
|
|
1089
|
+
(0, import_common7.Injectable)(),
|
|
1090
|
+
_ts_metadata7("design:type", Function),
|
|
1091
|
+
_ts_metadata7("design:paramtypes", [
|
|
1092
|
+
typeof import_nest4.AdminManifestService === "undefined" ? Object : import_nest4.AdminManifestService
|
|
1093
|
+
])
|
|
1094
|
+
], AdminManifestDoctorCheck);
|
|
1095
|
+
var PLATFORM_DOCTOR_CHECK_PROVIDERS = [
|
|
1096
|
+
PlanCatalogDoctorCheck,
|
|
1097
|
+
DiscoverySnapshotDoctorCheck,
|
|
1098
|
+
UserPortDoctorCheck,
|
|
1099
|
+
AdminManifestDoctorCheck
|
|
1100
|
+
];
|
|
1101
|
+
|
|
1102
|
+
// src/schema-apply.ts
|
|
1103
|
+
function stripLineComment(line) {
|
|
1104
|
+
const commentStart = line.indexOf("//");
|
|
1105
|
+
return commentStart === -1 ? line : line.slice(0, commentStart);
|
|
1106
|
+
}
|
|
1107
|
+
__name(stripLineComment, "stripLineComment");
|
|
1108
|
+
function extractModelNames(schema) {
|
|
1109
|
+
const names = [];
|
|
1110
|
+
const lines = schema.split("\n");
|
|
1111
|
+
for (const line of lines) {
|
|
1112
|
+
const stripped = stripLineComment(line).trim();
|
|
1113
|
+
const match = stripped.match(/^model\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/);
|
|
1114
|
+
if (match) names.push(match[1]);
|
|
1115
|
+
}
|
|
1116
|
+
return names;
|
|
1117
|
+
}
|
|
1118
|
+
__name(extractModelNames, "extractModelNames");
|
|
1119
|
+
function extractModelBlocks(fragment) {
|
|
1120
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1121
|
+
const lines = fragment.split("\n");
|
|
1122
|
+
let current = null;
|
|
1123
|
+
for (const rawLine of lines) {
|
|
1124
|
+
const stripped = stripLineComment(rawLine);
|
|
1125
|
+
if (!current) {
|
|
1126
|
+
const match = rawLine.match(/^\s*model\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{/);
|
|
1127
|
+
if (match) {
|
|
1128
|
+
const openCount = (stripped.match(/\{/g) ?? []).length;
|
|
1129
|
+
const closeCount = (stripped.match(/\}/g) ?? []).length;
|
|
1130
|
+
current = {
|
|
1131
|
+
name: match[1],
|
|
1132
|
+
lines: [
|
|
1133
|
+
rawLine
|
|
1134
|
+
],
|
|
1135
|
+
depth: openCount - closeCount
|
|
1136
|
+
};
|
|
1137
|
+
if (current.depth === 0) {
|
|
1138
|
+
blocks.set(current.name, current.lines.join("\n"));
|
|
1139
|
+
current = null;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
current.lines.push(rawLine);
|
|
1145
|
+
current.depth += (stripped.match(/\{/g) ?? []).length;
|
|
1146
|
+
current.depth -= (stripped.match(/\}/g) ?? []).length;
|
|
1147
|
+
if (current.depth <= 0) {
|
|
1148
|
+
blocks.set(current.name, current.lines.join("\n"));
|
|
1149
|
+
current = null;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return blocks;
|
|
1153
|
+
}
|
|
1154
|
+
__name(extractModelBlocks, "extractModelBlocks");
|
|
1155
|
+
function applyFragmentBlocks(schema, fragmentBlocks, options = {}) {
|
|
1156
|
+
const existing = new Set(extractModelNames(schema));
|
|
1157
|
+
const added = [];
|
|
1158
|
+
const skipped = [];
|
|
1159
|
+
const additions = [];
|
|
1160
|
+
for (const [name, block] of fragmentBlocks) {
|
|
1161
|
+
if (existing.has(name)) {
|
|
1162
|
+
skipped.push(name);
|
|
1163
|
+
} else {
|
|
1164
|
+
added.push(name);
|
|
1165
|
+
additions.push(block);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
if (additions.length === 0) {
|
|
1169
|
+
return {
|
|
1170
|
+
added,
|
|
1171
|
+
skipped,
|
|
1172
|
+
schema
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
const header = options.fragmentLabel ? `
|
|
1176
|
+
|
|
1177
|
+
// ============================================================
|
|
1178
|
+
// Eingef\xFCgt durch \`saas-platform schema apply\` aus ${options.fragmentLabel}
|
|
1179
|
+
// ============================================================
|
|
1180
|
+
` : `
|
|
1181
|
+
|
|
1182
|
+
// Eingef\xFCgt durch \`saas-platform schema apply\`
|
|
1183
|
+
`;
|
|
1184
|
+
const trimmedSchema = schema.endsWith("\n") ? schema : schema + "\n";
|
|
1185
|
+
return {
|
|
1186
|
+
added,
|
|
1187
|
+
skipped,
|
|
1188
|
+
schema: trimmedSchema + header + additions.join("\n\n") + "\n"
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
__name(applyFragmentBlocks, "applyFragmentBlocks");
|
|
1192
|
+
|
|
1193
|
+
// src/module.ts
|
|
1194
|
+
var import_common8 = require("@nestjs/common");
|
|
1195
|
+
var import_nest5 = require("@saasicat/nest");
|
|
1196
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
1197
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1198
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1199
|
+
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;
|
|
1200
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1201
|
+
}
|
|
1202
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
1203
|
+
var CliContextModule = class _CliContextModule {
|
|
1204
|
+
static {
|
|
1205
|
+
__name(this, "CliContextModule");
|
|
1206
|
+
}
|
|
1207
|
+
static forRoot(options) {
|
|
1208
|
+
const providers = [
|
|
1209
|
+
{
|
|
1210
|
+
provide: CLI_CONTEXT_CONFIG_TOKEN,
|
|
1211
|
+
useValue: options.config
|
|
1212
|
+
},
|
|
1213
|
+
(0, import_nest5.asProvider)(USER_PORT_TOKEN, options.userPort),
|
|
1214
|
+
CliContextService,
|
|
1215
|
+
MfaSetupFlow,
|
|
1216
|
+
WhoAmIFlow
|
|
1217
|
+
];
|
|
1218
|
+
const exports_ = [
|
|
1219
|
+
CLI_CONTEXT_CONFIG_TOKEN,
|
|
1220
|
+
CliContextService,
|
|
1221
|
+
MfaSetupFlow,
|
|
1222
|
+
WhoAmIFlow
|
|
1223
|
+
];
|
|
1224
|
+
if (options.auditQueryPort) {
|
|
1225
|
+
providers.push((0, import_nest5.asProvider)(AUDIT_QUERY_PORT_TOKEN, options.auditQueryPort));
|
|
1226
|
+
providers.push(AuditTailFlow);
|
|
1227
|
+
exports_.push(AuditTailFlow);
|
|
1228
|
+
}
|
|
1229
|
+
if (options.defaultDoctorChecks) {
|
|
1230
|
+
providers.push(...PLATFORM_DOCTOR_CHECK_PROVIDERS);
|
|
1231
|
+
providers.push({
|
|
1232
|
+
provide: DOCTOR_CHECKS_TOKEN,
|
|
1233
|
+
useFactory: /* @__PURE__ */ __name((...platformChecks) => {
|
|
1234
|
+
const extra = Array.isArray(options.doctorChecks) ? options.doctorChecks : [];
|
|
1235
|
+
return [
|
|
1236
|
+
...platformChecks,
|
|
1237
|
+
...extra
|
|
1238
|
+
];
|
|
1239
|
+
}, "useFactory"),
|
|
1240
|
+
inject: PLATFORM_DOCTOR_CHECK_PROVIDERS
|
|
1241
|
+
});
|
|
1242
|
+
} else {
|
|
1243
|
+
providers.push((0, import_nest5.asProvider)(DOCTOR_CHECKS_TOKEN, options.doctorChecks ?? []));
|
|
1244
|
+
}
|
|
1245
|
+
providers.push(DoctorFlow);
|
|
1246
|
+
exports_.push(DoctorFlow);
|
|
1247
|
+
if (options.manifestAccessPort) {
|
|
1248
|
+
providers.push((0, import_nest5.asProvider)(MANIFEST_ACCESS_PORT_TOKEN, options.manifestAccessPort), (0, import_nest5.asProvider)(MANIFEST_CHECKS_TOKEN, options.manifestChecks ?? DEFAULT_MANIFEST_CHECKS), ManifestCliFlow);
|
|
1249
|
+
exports_.push(ManifestCliFlow);
|
|
1250
|
+
}
|
|
1251
|
+
if (options.userManagementPort) {
|
|
1252
|
+
providers.push((0, import_nest5.asProvider)(USER_MANAGEMENT_PORT_TOKEN, options.userManagementPort));
|
|
1253
|
+
exports_.push(USER_MANAGEMENT_PORT_TOKEN);
|
|
1254
|
+
}
|
|
1255
|
+
return {
|
|
1256
|
+
module: _CliContextModule,
|
|
1257
|
+
global: options.global ?? false,
|
|
1258
|
+
providers,
|
|
1259
|
+
exports: exports_
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
CliContextModule = _ts_decorate8([
|
|
1264
|
+
(0, import_common8.Module)({})
|
|
1265
|
+
], CliContextModule);
|
|
1266
|
+
|
|
1267
|
+
// src/manifest.command.ts
|
|
1268
|
+
var import_common9 = require("@nestjs/common");
|
|
1269
|
+
var import_nest_commander = require("nest-commander");
|
|
1270
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
1271
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1272
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1273
|
+
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;
|
|
1274
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1275
|
+
}
|
|
1276
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
1277
|
+
function _ts_metadata8(k, v) {
|
|
1278
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1279
|
+
}
|
|
1280
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
1281
|
+
var ManifestDumpCommand = class extends import_nest_commander.CommandRunner {
|
|
1282
|
+
static {
|
|
1283
|
+
__name(this, "ManifestDumpCommand");
|
|
1284
|
+
}
|
|
1285
|
+
ctx;
|
|
1286
|
+
flow;
|
|
1287
|
+
constructor(ctx, flow) {
|
|
1288
|
+
super(), this.ctx = ctx, this.flow = flow;
|
|
1289
|
+
}
|
|
1290
|
+
async run(_args, flags) {
|
|
1291
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1292
|
+
await this.ctx.ensureSuperAdmin(identity);
|
|
1293
|
+
process.stdout.write(JSON.stringify(this.flow.dump(), null, 2) + "\n");
|
|
1294
|
+
}
|
|
1295
|
+
parseAs(v) {
|
|
1296
|
+
return v;
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
_ts_decorate9([
|
|
1300
|
+
(0, import_nest_commander.Option)({
|
|
1301
|
+
flags: "--as <email>"
|
|
1302
|
+
}),
|
|
1303
|
+
_ts_metadata8("design:type", Function),
|
|
1304
|
+
_ts_metadata8("design:paramtypes", [
|
|
1305
|
+
String
|
|
1306
|
+
]),
|
|
1307
|
+
_ts_metadata8("design:returntype", String)
|
|
1308
|
+
], ManifestDumpCommand.prototype, "parseAs", null);
|
|
1309
|
+
ManifestDumpCommand = _ts_decorate9([
|
|
1310
|
+
(0, import_common9.Injectable)(),
|
|
1311
|
+
(0, import_nest_commander.SubCommand)({
|
|
1312
|
+
name: "dump",
|
|
1313
|
+
description: "Manifest als JSON ausgeben"
|
|
1314
|
+
}),
|
|
1315
|
+
_ts_metadata8("design:type", Function),
|
|
1316
|
+
_ts_metadata8("design:paramtypes", [
|
|
1317
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
1318
|
+
typeof ManifestCliFlow === "undefined" ? Object : ManifestCliFlow
|
|
1319
|
+
])
|
|
1320
|
+
], ManifestDumpCommand);
|
|
1321
|
+
var ManifestHashCommand = class extends import_nest_commander.CommandRunner {
|
|
1322
|
+
static {
|
|
1323
|
+
__name(this, "ManifestHashCommand");
|
|
1324
|
+
}
|
|
1325
|
+
ctx;
|
|
1326
|
+
flow;
|
|
1327
|
+
constructor(ctx, flow) {
|
|
1328
|
+
super(), this.ctx = ctx, this.flow = flow;
|
|
1329
|
+
}
|
|
1330
|
+
async run(_args, flags) {
|
|
1331
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1332
|
+
await this.ctx.ensureSuperAdmin(identity);
|
|
1333
|
+
process.stdout.write(this.flow.hash() + "\n");
|
|
1334
|
+
}
|
|
1335
|
+
parseAs(v) {
|
|
1336
|
+
return v;
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
_ts_decorate9([
|
|
1340
|
+
(0, import_nest_commander.Option)({
|
|
1341
|
+
flags: "--as <email>"
|
|
1342
|
+
}),
|
|
1343
|
+
_ts_metadata8("design:type", Function),
|
|
1344
|
+
_ts_metadata8("design:paramtypes", [
|
|
1345
|
+
String
|
|
1346
|
+
]),
|
|
1347
|
+
_ts_metadata8("design:returntype", String)
|
|
1348
|
+
], ManifestHashCommand.prototype, "parseAs", null);
|
|
1349
|
+
ManifestHashCommand = _ts_decorate9([
|
|
1350
|
+
(0, import_common9.Injectable)(),
|
|
1351
|
+
(0, import_nest_commander.SubCommand)({
|
|
1352
|
+
name: "hash",
|
|
1353
|
+
description: "manifestHash ausgeben (CI-Pinning)"
|
|
1354
|
+
}),
|
|
1355
|
+
_ts_metadata8("design:type", Function),
|
|
1356
|
+
_ts_metadata8("design:paramtypes", [
|
|
1357
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
1358
|
+
typeof ManifestCliFlow === "undefined" ? Object : ManifestCliFlow
|
|
1359
|
+
])
|
|
1360
|
+
], ManifestHashCommand);
|
|
1361
|
+
var ManifestValidateCommand = class extends import_nest_commander.CommandRunner {
|
|
1362
|
+
static {
|
|
1363
|
+
__name(this, "ManifestValidateCommand");
|
|
1364
|
+
}
|
|
1365
|
+
ctx;
|
|
1366
|
+
flow;
|
|
1367
|
+
constructor(ctx, flow) {
|
|
1368
|
+
super(), this.ctx = ctx, this.flow = flow;
|
|
1369
|
+
}
|
|
1370
|
+
async run(_args, flags) {
|
|
1371
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1372
|
+
await this.ctx.ensureSuperAdmin(identity);
|
|
1373
|
+
const result = this.flow.validate();
|
|
1374
|
+
if (result.ok) {
|
|
1375
|
+
process.stdout.write("Manifest validiert \u2713\n");
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
process.stderr.write(`Manifest invalid: ${result.reason}
|
|
1379
|
+
`);
|
|
1380
|
+
process.exit(1);
|
|
1381
|
+
}
|
|
1382
|
+
parseAs(v) {
|
|
1383
|
+
return v;
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
_ts_decorate9([
|
|
1387
|
+
(0, import_nest_commander.Option)({
|
|
1388
|
+
flags: "--as <email>"
|
|
1389
|
+
}),
|
|
1390
|
+
_ts_metadata8("design:type", Function),
|
|
1391
|
+
_ts_metadata8("design:paramtypes", [
|
|
1392
|
+
String
|
|
1393
|
+
]),
|
|
1394
|
+
_ts_metadata8("design:returntype", String)
|
|
1395
|
+
], ManifestValidateCommand.prototype, "parseAs", null);
|
|
1396
|
+
ManifestValidateCommand = _ts_decorate9([
|
|
1397
|
+
(0, import_common9.Injectable)(),
|
|
1398
|
+
(0, import_nest_commander.SubCommand)({
|
|
1399
|
+
name: "validate",
|
|
1400
|
+
description: "Schnell-Sanity (schemaVersion + project.key + manifestHash)"
|
|
1401
|
+
}),
|
|
1402
|
+
_ts_metadata8("design:type", Function),
|
|
1403
|
+
_ts_metadata8("design:paramtypes", [
|
|
1404
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
1405
|
+
typeof ManifestCliFlow === "undefined" ? Object : ManifestCliFlow
|
|
1406
|
+
])
|
|
1407
|
+
], ManifestValidateCommand);
|
|
1408
|
+
var ManifestCheckCommand = class extends import_nest_commander.CommandRunner {
|
|
1409
|
+
static {
|
|
1410
|
+
__name(this, "ManifestCheckCommand");
|
|
1411
|
+
}
|
|
1412
|
+
ctx;
|
|
1413
|
+
flow;
|
|
1414
|
+
constructor(ctx, flow) {
|
|
1415
|
+
super(), this.ctx = ctx, this.flow = flow;
|
|
1416
|
+
}
|
|
1417
|
+
async run(_args, flags) {
|
|
1418
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1419
|
+
await this.ctx.ensureSuperAdmin(identity);
|
|
1420
|
+
const report = await this.flow.runChecks();
|
|
1421
|
+
process.stdout.write(this.flow.formatReport(report) + "\n");
|
|
1422
|
+
const code = this.flow.exitCodeFor(report);
|
|
1423
|
+
if (code !== 0) process.exit(code);
|
|
1424
|
+
}
|
|
1425
|
+
parseAs(v) {
|
|
1426
|
+
return v;
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
_ts_decorate9([
|
|
1430
|
+
(0, import_nest_commander.Option)({
|
|
1431
|
+
flags: "--as <email>"
|
|
1432
|
+
}),
|
|
1433
|
+
_ts_metadata8("design:type", Function),
|
|
1434
|
+
_ts_metadata8("design:paramtypes", [
|
|
1435
|
+
String
|
|
1436
|
+
]),
|
|
1437
|
+
_ts_metadata8("design:returntype", String)
|
|
1438
|
+
], ManifestCheckCommand.prototype, "parseAs", null);
|
|
1439
|
+
ManifestCheckCommand = _ts_decorate9([
|
|
1440
|
+
(0, import_common9.Injectable)(),
|
|
1441
|
+
(0, import_nest_commander.SubCommand)({
|
|
1442
|
+
name: "check",
|
|
1443
|
+
description: "Alle Manifest-Checks (Exit-Code 7 bei error/Drift)"
|
|
1444
|
+
}),
|
|
1445
|
+
_ts_metadata8("design:type", Function),
|
|
1446
|
+
_ts_metadata8("design:paramtypes", [
|
|
1447
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
1448
|
+
typeof ManifestCliFlow === "undefined" ? Object : ManifestCliFlow
|
|
1449
|
+
])
|
|
1450
|
+
], ManifestCheckCommand);
|
|
1451
|
+
var ManifestCommands = class extends import_nest_commander.CommandRunner {
|
|
1452
|
+
static {
|
|
1453
|
+
__name(this, "ManifestCommands");
|
|
1454
|
+
}
|
|
1455
|
+
async run() {
|
|
1456
|
+
process.stderr.write("Bitte Sub-Command angeben: dump, hash, validate, check.\n");
|
|
1457
|
+
process.exit(2);
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
ManifestCommands = _ts_decorate9([
|
|
1461
|
+
(0, import_common9.Injectable)(),
|
|
1462
|
+
(0, import_nest_commander.Command)({
|
|
1463
|
+
name: "manifest",
|
|
1464
|
+
description: "Manifest-Operations (dump, hash, validate, check)",
|
|
1465
|
+
subCommands: [
|
|
1466
|
+
ManifestDumpCommand,
|
|
1467
|
+
ManifestHashCommand,
|
|
1468
|
+
ManifestValidateCommand,
|
|
1469
|
+
ManifestCheckCommand
|
|
1470
|
+
]
|
|
1471
|
+
})
|
|
1472
|
+
], ManifestCommands);
|
|
1473
|
+
|
|
1474
|
+
// src/admin.command.ts
|
|
1475
|
+
var import_common10 = require("@nestjs/common");
|
|
1476
|
+
var import_nest_commander2 = require("nest-commander");
|
|
1477
|
+
var import_qrcode_terminal = __toESM(require("qrcode-terminal"), 1);
|
|
1478
|
+
function _ts_decorate10(decorators, target, key, desc) {
|
|
1479
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1480
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1481
|
+
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;
|
|
1482
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1483
|
+
}
|
|
1484
|
+
__name(_ts_decorate10, "_ts_decorate");
|
|
1485
|
+
function _ts_metadata9(k, v) {
|
|
1486
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1487
|
+
}
|
|
1488
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
1489
|
+
function _ts_param7(paramIndex, decorator) {
|
|
1490
|
+
return function(target, key) {
|
|
1491
|
+
decorator(target, key, paramIndex);
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
__name(_ts_param7, "_ts_param");
|
|
1495
|
+
var AdminWhoamiCommand = class extends import_nest_commander2.CommandRunner {
|
|
1496
|
+
static {
|
|
1497
|
+
__name(this, "AdminWhoamiCommand");
|
|
1498
|
+
}
|
|
1499
|
+
flow;
|
|
1500
|
+
constructor(flow) {
|
|
1501
|
+
super(), this.flow = flow;
|
|
1502
|
+
}
|
|
1503
|
+
async run(_args, flags) {
|
|
1504
|
+
const result = await this.flow.run(flags.as);
|
|
1505
|
+
process.stdout.write(this.flow.formatResult(result) + "\n");
|
|
1506
|
+
}
|
|
1507
|
+
parseAs(v) {
|
|
1508
|
+
return v;
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
_ts_decorate10([
|
|
1512
|
+
(0, import_nest_commander2.Option)({
|
|
1513
|
+
flags: "--as <email>"
|
|
1514
|
+
}),
|
|
1515
|
+
_ts_metadata9("design:type", Function),
|
|
1516
|
+
_ts_metadata9("design:paramtypes", [
|
|
1517
|
+
String
|
|
1518
|
+
]),
|
|
1519
|
+
_ts_metadata9("design:returntype", String)
|
|
1520
|
+
], AdminWhoamiCommand.prototype, "parseAs", null);
|
|
1521
|
+
AdminWhoamiCommand = _ts_decorate10([
|
|
1522
|
+
(0, import_common10.Injectable)(),
|
|
1523
|
+
(0, import_nest_commander2.SubCommand)({
|
|
1524
|
+
name: "whoami",
|
|
1525
|
+
description: "Aktive CLI-Identit\xE4t + MFA-/Production-Status"
|
|
1526
|
+
}),
|
|
1527
|
+
_ts_metadata9("design:type", Function),
|
|
1528
|
+
_ts_metadata9("design:paramtypes", [
|
|
1529
|
+
typeof WhoAmIFlow === "undefined" ? Object : WhoAmIFlow
|
|
1530
|
+
])
|
|
1531
|
+
], AdminWhoamiCommand);
|
|
1532
|
+
var AdminMfaSetupCommand = class extends import_nest_commander2.CommandRunner {
|
|
1533
|
+
static {
|
|
1534
|
+
__name(this, "AdminMfaSetupCommand");
|
|
1535
|
+
}
|
|
1536
|
+
config;
|
|
1537
|
+
flow;
|
|
1538
|
+
constructor(config, flow) {
|
|
1539
|
+
super(), this.config = config, this.flow = flow;
|
|
1540
|
+
}
|
|
1541
|
+
async run(_args, flags) {
|
|
1542
|
+
const result = await this.flow.run({
|
|
1543
|
+
asFlag: flags.as,
|
|
1544
|
+
issuer: this.config.mfaIssuer ?? "SuperAdmin",
|
|
1545
|
+
force: flags.force
|
|
1546
|
+
});
|
|
1547
|
+
await new Promise((resolve) => {
|
|
1548
|
+
import_qrcode_terminal.default.generate(result.otpauthUri, {
|
|
1549
|
+
small: true
|
|
1550
|
+
}, (qr) => {
|
|
1551
|
+
process.stdout.write("\n" + qr + "\n");
|
|
1552
|
+
resolve();
|
|
1553
|
+
});
|
|
1554
|
+
});
|
|
1555
|
+
process.stdout.write(this.flow.formatSetupResult(result) + "\n");
|
|
1556
|
+
}
|
|
1557
|
+
parseAs(v) {
|
|
1558
|
+
return v;
|
|
1559
|
+
}
|
|
1560
|
+
parseForce() {
|
|
1561
|
+
return true;
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
_ts_decorate10([
|
|
1565
|
+
(0, import_nest_commander2.Option)({
|
|
1566
|
+
flags: "--as <email>"
|
|
1567
|
+
}),
|
|
1568
|
+
_ts_metadata9("design:type", Function),
|
|
1569
|
+
_ts_metadata9("design:paramtypes", [
|
|
1570
|
+
String
|
|
1571
|
+
]),
|
|
1572
|
+
_ts_metadata9("design:returntype", String)
|
|
1573
|
+
], AdminMfaSetupCommand.prototype, "parseAs", null);
|
|
1574
|
+
_ts_decorate10([
|
|
1575
|
+
(0, import_nest_commander2.Option)({
|
|
1576
|
+
flags: "--force",
|
|
1577
|
+
description: "bestehendes Secret ohne R\xFCckfrage \xFCberschreiben"
|
|
1578
|
+
}),
|
|
1579
|
+
_ts_metadata9("design:type", Function),
|
|
1580
|
+
_ts_metadata9("design:paramtypes", []),
|
|
1581
|
+
_ts_metadata9("design:returntype", Boolean)
|
|
1582
|
+
], AdminMfaSetupCommand.prototype, "parseForce", null);
|
|
1583
|
+
AdminMfaSetupCommand = _ts_decorate10([
|
|
1584
|
+
(0, import_common10.Injectable)(),
|
|
1585
|
+
(0, import_nest_commander2.SubCommand)({
|
|
1586
|
+
name: "mfa-setup",
|
|
1587
|
+
description: "TOTP-MFA f\xFCr den eigenen SuperAdmin einrichten"
|
|
1588
|
+
}),
|
|
1589
|
+
_ts_param7(0, (0, import_common10.Inject)(CLI_CONTEXT_CONFIG_TOKEN)),
|
|
1590
|
+
_ts_metadata9("design:type", Function),
|
|
1591
|
+
_ts_metadata9("design:paramtypes", [
|
|
1592
|
+
typeof CliContextConfig === "undefined" ? Object : CliContextConfig,
|
|
1593
|
+
typeof MfaSetupFlow === "undefined" ? Object : MfaSetupFlow
|
|
1594
|
+
])
|
|
1595
|
+
], AdminMfaSetupCommand);
|
|
1596
|
+
var AdminCommands = class extends import_nest_commander2.CommandRunner {
|
|
1597
|
+
static {
|
|
1598
|
+
__name(this, "AdminCommands");
|
|
1599
|
+
}
|
|
1600
|
+
async run() {
|
|
1601
|
+
process.stderr.write("Bitte Sub-Command angeben: whoami, mfa-setup.\n");
|
|
1602
|
+
process.exit(2);
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
AdminCommands = _ts_decorate10([
|
|
1606
|
+
(0, import_common10.Injectable)(),
|
|
1607
|
+
(0, import_nest_commander2.Command)({
|
|
1608
|
+
name: "admin",
|
|
1609
|
+
description: "SuperAdmin-Operations (whoami, mfa-setup)",
|
|
1610
|
+
subCommands: [
|
|
1611
|
+
AdminWhoamiCommand,
|
|
1612
|
+
AdminMfaSetupCommand
|
|
1613
|
+
]
|
|
1614
|
+
})
|
|
1615
|
+
], AdminCommands);
|
|
1616
|
+
|
|
1617
|
+
// src/audit.command.ts
|
|
1618
|
+
var import_common11 = require("@nestjs/common");
|
|
1619
|
+
var import_nest_commander3 = require("nest-commander");
|
|
1620
|
+
function _ts_decorate11(decorators, target, key, desc) {
|
|
1621
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1622
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1623
|
+
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;
|
|
1624
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1625
|
+
}
|
|
1626
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
1627
|
+
function _ts_metadata10(k, v) {
|
|
1628
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1629
|
+
}
|
|
1630
|
+
__name(_ts_metadata10, "_ts_metadata");
|
|
1631
|
+
var AuditTailCommand = class extends import_nest_commander3.CommandRunner {
|
|
1632
|
+
static {
|
|
1633
|
+
__name(this, "AuditTailCommand");
|
|
1634
|
+
}
|
|
1635
|
+
ctx;
|
|
1636
|
+
flow;
|
|
1637
|
+
constructor(ctx, flow) {
|
|
1638
|
+
super(), this.ctx = ctx, this.flow = flow;
|
|
1639
|
+
}
|
|
1640
|
+
async run(_args, flags) {
|
|
1641
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1642
|
+
await this.ctx.ensureSuperAdmin(identity);
|
|
1643
|
+
const entries = await this.flow.run({
|
|
1644
|
+
actor: flags.actor,
|
|
1645
|
+
action: flags.action,
|
|
1646
|
+
entity: flags.entity,
|
|
1647
|
+
since: flags.since,
|
|
1648
|
+
limit: flags.limit
|
|
1649
|
+
});
|
|
1650
|
+
this.ctx.table(this.flow.formatRows(entries));
|
|
1651
|
+
}
|
|
1652
|
+
parseAs(v) {
|
|
1653
|
+
return v;
|
|
1654
|
+
}
|
|
1655
|
+
parseActor(v) {
|
|
1656
|
+
return v;
|
|
1657
|
+
}
|
|
1658
|
+
parseAction(v) {
|
|
1659
|
+
return v;
|
|
1660
|
+
}
|
|
1661
|
+
parseEntity(v) {
|
|
1662
|
+
return v;
|
|
1663
|
+
}
|
|
1664
|
+
parseSince(v) {
|
|
1665
|
+
return v;
|
|
1666
|
+
}
|
|
1667
|
+
parseLimit(v) {
|
|
1668
|
+
return Number.parseInt(v, 10);
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
_ts_decorate11([
|
|
1672
|
+
(0, import_nest_commander3.Option)({
|
|
1673
|
+
flags: "--as <email>"
|
|
1674
|
+
}),
|
|
1675
|
+
_ts_metadata10("design:type", Function),
|
|
1676
|
+
_ts_metadata10("design:paramtypes", [
|
|
1677
|
+
String
|
|
1678
|
+
]),
|
|
1679
|
+
_ts_metadata10("design:returntype", String)
|
|
1680
|
+
], AuditTailCommand.prototype, "parseAs", null);
|
|
1681
|
+
_ts_decorate11([
|
|
1682
|
+
(0, import_nest_commander3.Option)({
|
|
1683
|
+
flags: "--actor <email>"
|
|
1684
|
+
}),
|
|
1685
|
+
_ts_metadata10("design:type", Function),
|
|
1686
|
+
_ts_metadata10("design:paramtypes", [
|
|
1687
|
+
String
|
|
1688
|
+
]),
|
|
1689
|
+
_ts_metadata10("design:returntype", String)
|
|
1690
|
+
], AuditTailCommand.prototype, "parseActor", null);
|
|
1691
|
+
_ts_decorate11([
|
|
1692
|
+
(0, import_nest_commander3.Option)({
|
|
1693
|
+
flags: "--action <name>"
|
|
1694
|
+
}),
|
|
1695
|
+
_ts_metadata10("design:type", Function),
|
|
1696
|
+
_ts_metadata10("design:paramtypes", [
|
|
1697
|
+
String
|
|
1698
|
+
]),
|
|
1699
|
+
_ts_metadata10("design:returntype", String)
|
|
1700
|
+
], AuditTailCommand.prototype, "parseAction", null);
|
|
1701
|
+
_ts_decorate11([
|
|
1702
|
+
(0, import_nest_commander3.Option)({
|
|
1703
|
+
flags: "--entity <name>"
|
|
1704
|
+
}),
|
|
1705
|
+
_ts_metadata10("design:type", Function),
|
|
1706
|
+
_ts_metadata10("design:paramtypes", [
|
|
1707
|
+
String
|
|
1708
|
+
]),
|
|
1709
|
+
_ts_metadata10("design:returntype", String)
|
|
1710
|
+
], AuditTailCommand.prototype, "parseEntity", null);
|
|
1711
|
+
_ts_decorate11([
|
|
1712
|
+
(0, import_nest_commander3.Option)({
|
|
1713
|
+
flags: "--since <iso-date>"
|
|
1714
|
+
}),
|
|
1715
|
+
_ts_metadata10("design:type", Function),
|
|
1716
|
+
_ts_metadata10("design:paramtypes", [
|
|
1717
|
+
String
|
|
1718
|
+
]),
|
|
1719
|
+
_ts_metadata10("design:returntype", String)
|
|
1720
|
+
], AuditTailCommand.prototype, "parseSince", null);
|
|
1721
|
+
_ts_decorate11([
|
|
1722
|
+
(0, import_nest_commander3.Option)({
|
|
1723
|
+
flags: "--limit <n>"
|
|
1724
|
+
}),
|
|
1725
|
+
_ts_metadata10("design:type", Function),
|
|
1726
|
+
_ts_metadata10("design:paramtypes", [
|
|
1727
|
+
String
|
|
1728
|
+
]),
|
|
1729
|
+
_ts_metadata10("design:returntype", Number)
|
|
1730
|
+
], AuditTailCommand.prototype, "parseLimit", null);
|
|
1731
|
+
AuditTailCommand = _ts_decorate11([
|
|
1732
|
+
(0, import_common11.Injectable)(),
|
|
1733
|
+
(0, import_nest_commander3.SubCommand)({
|
|
1734
|
+
name: "tail",
|
|
1735
|
+
description: "Letzte Audit-Log-Eintr\xE4ge (--actor/--action/--entity/--since/--limit)"
|
|
1736
|
+
}),
|
|
1737
|
+
_ts_metadata10("design:type", Function),
|
|
1738
|
+
_ts_metadata10("design:paramtypes", [
|
|
1739
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
1740
|
+
typeof AuditTailFlow === "undefined" ? Object : AuditTailFlow
|
|
1741
|
+
])
|
|
1742
|
+
], AuditTailCommand);
|
|
1743
|
+
var AuditCommands = class extends import_nest_commander3.CommandRunner {
|
|
1744
|
+
static {
|
|
1745
|
+
__name(this, "AuditCommands");
|
|
1746
|
+
}
|
|
1747
|
+
async run() {
|
|
1748
|
+
process.stderr.write("Bitte Sub-Command angeben: tail.\n");
|
|
1749
|
+
process.exit(2);
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
AuditCommands = _ts_decorate11([
|
|
1753
|
+
(0, import_common11.Injectable)(),
|
|
1754
|
+
(0, import_nest_commander3.Command)({
|
|
1755
|
+
name: "audit",
|
|
1756
|
+
description: "Audit-Log-Operations (tail)",
|
|
1757
|
+
subCommands: [
|
|
1758
|
+
AuditTailCommand
|
|
1759
|
+
]
|
|
1760
|
+
})
|
|
1761
|
+
], AuditCommands);
|
|
1762
|
+
|
|
1763
|
+
// src/doctor.command.ts
|
|
1764
|
+
var import_common12 = require("@nestjs/common");
|
|
1765
|
+
var import_nest_commander4 = require("nest-commander");
|
|
1766
|
+
function _ts_decorate12(decorators, target, key, desc) {
|
|
1767
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1768
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1769
|
+
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;
|
|
1770
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1771
|
+
}
|
|
1772
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
1773
|
+
function _ts_metadata11(k, v) {
|
|
1774
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1775
|
+
}
|
|
1776
|
+
__name(_ts_metadata11, "_ts_metadata");
|
|
1777
|
+
var DoctorCommands = class extends import_nest_commander4.CommandRunner {
|
|
1778
|
+
static {
|
|
1779
|
+
__name(this, "DoctorCommands");
|
|
1780
|
+
}
|
|
1781
|
+
ctx;
|
|
1782
|
+
flow;
|
|
1783
|
+
constructor(ctx, flow) {
|
|
1784
|
+
super(), this.ctx = ctx, this.flow = flow;
|
|
1785
|
+
}
|
|
1786
|
+
async run(_args, flags) {
|
|
1787
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1788
|
+
await this.ctx.ensureSuperAdmin(identity);
|
|
1789
|
+
const report = await this.flow.run();
|
|
1790
|
+
process.stdout.write(this.flow.formatReport(report) + "\n");
|
|
1791
|
+
const code = this.flow.exitCodeFor(report);
|
|
1792
|
+
if (code !== 0) process.exit(code);
|
|
1793
|
+
}
|
|
1794
|
+
parseAs(v) {
|
|
1795
|
+
return v;
|
|
1796
|
+
}
|
|
1797
|
+
};
|
|
1798
|
+
_ts_decorate12([
|
|
1799
|
+
(0, import_nest_commander4.Option)({
|
|
1800
|
+
flags: "--as <email>"
|
|
1801
|
+
}),
|
|
1802
|
+
_ts_metadata11("design:type", Function),
|
|
1803
|
+
_ts_metadata11("design:paramtypes", [
|
|
1804
|
+
String
|
|
1805
|
+
]),
|
|
1806
|
+
_ts_metadata11("design:returntype", String)
|
|
1807
|
+
], DoctorCommands.prototype, "parseAs", null);
|
|
1808
|
+
DoctorCommands = _ts_decorate12([
|
|
1809
|
+
(0, import_common12.Injectable)(),
|
|
1810
|
+
(0, import_nest_commander4.Command)({
|
|
1811
|
+
name: "doctor",
|
|
1812
|
+
description: "Health-/Drift-Checks (Exit-Code 4 bei error)"
|
|
1813
|
+
}),
|
|
1814
|
+
_ts_metadata11("design:type", Function),
|
|
1815
|
+
_ts_metadata11("design:paramtypes", [
|
|
1816
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
1817
|
+
typeof DoctorFlow === "undefined" ? Object : DoctorFlow
|
|
1818
|
+
])
|
|
1819
|
+
], DoctorCommands);
|
|
1820
|
+
|
|
1821
|
+
// src/discovery.command.ts
|
|
1822
|
+
var import_node_fs = require("fs");
|
|
1823
|
+
var import_node_path = require("path");
|
|
1824
|
+
var import_common13 = require("@nestjs/common");
|
|
1825
|
+
var import_nest_commander5 = require("nest-commander");
|
|
1826
|
+
var import_nest6 = require("@saasicat/nest");
|
|
1827
|
+
function _ts_decorate13(decorators, target, key, desc) {
|
|
1828
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1829
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1830
|
+
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;
|
|
1831
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1832
|
+
}
|
|
1833
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
1834
|
+
function _ts_metadata12(k, v) {
|
|
1835
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1836
|
+
}
|
|
1837
|
+
__name(_ts_metadata12, "_ts_metadata");
|
|
1838
|
+
function _ts_param8(paramIndex, decorator) {
|
|
1839
|
+
return function(target, key) {
|
|
1840
|
+
decorator(target, key, paramIndex);
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
__name(_ts_param8, "_ts_param");
|
|
1844
|
+
var DiscoveryScanCommand = class extends import_nest_commander5.CommandRunner {
|
|
1845
|
+
static {
|
|
1846
|
+
__name(this, "DiscoveryScanCommand");
|
|
1847
|
+
}
|
|
1848
|
+
scanner;
|
|
1849
|
+
configuredPath;
|
|
1850
|
+
constructor(scanner = null, configuredPath = null) {
|
|
1851
|
+
super(), this.scanner = scanner, this.configuredPath = configuredPath;
|
|
1852
|
+
}
|
|
1853
|
+
async run(_args, flags) {
|
|
1854
|
+
if (!this.scanner) {
|
|
1855
|
+
this.fail("DiscoveryScanner nicht registriert \u2014 DiscoveryModule.forRoot() im CLI-Modul importieren.", flags);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
try {
|
|
1859
|
+
const snapshot = this.scanner.rebuildSnapshot();
|
|
1860
|
+
if (flags.out) {
|
|
1861
|
+
const outPath = (0, import_node_path.resolve)(flags.out);
|
|
1862
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(outPath), {
|
|
1863
|
+
recursive: true
|
|
1864
|
+
});
|
|
1865
|
+
(0, import_node_fs.writeFileSync)(outPath, JSON.stringify(snapshot, null, 2) + "\n", "utf-8");
|
|
1866
|
+
}
|
|
1867
|
+
const target = flags.out ?? this.configuredPath;
|
|
1868
|
+
process.stdout.write(`Discovery-Scan (${snapshot.app.key} v${snapshot.app.version}): ${snapshot.capabilities.length} Capabilities \xB7 ${snapshot.features.length} Features \xB7 ${snapshot.quotas.length} Quotas \xB7 hash ${snapshot.hash.slice(0, 19)}\u2026
|
|
1869
|
+
`);
|
|
1870
|
+
if (target) {
|
|
1871
|
+
process.stdout.write(`Snapshot persistiert: ${(0, import_node_path.resolve)(target)}
|
|
1872
|
+
`);
|
|
1873
|
+
} else {
|
|
1874
|
+
process.stderr.write("WARNUNG: Snapshot wurde nicht persistiert \u2014 weder snapshotPath (DiscoveryModule.forRoot) konfiguriert noch --out angegeben. Das Seed-Gate findet so keinen Snapshot.\n");
|
|
1875
|
+
}
|
|
1876
|
+
} catch (err2) {
|
|
1877
|
+
this.fail(err2 instanceof Error ? err2.message : String(err2), flags);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
/** Exit 4 (analogous to Seed-Gate/Preflight) — only a warning with `--non-fatal`. */
|
|
1881
|
+
fail(message, flags) {
|
|
1882
|
+
if (flags.nonFatal) {
|
|
1883
|
+
process.stderr.write(`[discovery scan] WARN (non-fatal): ${message}
|
|
1884
|
+
`);
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
process.stderr.write(`[discovery scan] FEHLER: ${message}
|
|
1888
|
+
`);
|
|
1889
|
+
process.exit(4);
|
|
1890
|
+
}
|
|
1891
|
+
parseOut(v) {
|
|
1892
|
+
return v;
|
|
1893
|
+
}
|
|
1894
|
+
parseNonFatal() {
|
|
1895
|
+
return true;
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
_ts_decorate13([
|
|
1899
|
+
(0, import_nest_commander5.Option)({
|
|
1900
|
+
flags: "--out <path>",
|
|
1901
|
+
description: "Snapshot zus\xE4tzlich an diesen Pfad schreiben"
|
|
1902
|
+
}),
|
|
1903
|
+
_ts_metadata12("design:type", Function),
|
|
1904
|
+
_ts_metadata12("design:paramtypes", [
|
|
1905
|
+
String
|
|
1906
|
+
]),
|
|
1907
|
+
_ts_metadata12("design:returntype", String)
|
|
1908
|
+
], DiscoveryScanCommand.prototype, "parseOut", null);
|
|
1909
|
+
_ts_decorate13([
|
|
1910
|
+
(0, import_nest_commander5.Option)({
|
|
1911
|
+
flags: "--non-fatal",
|
|
1912
|
+
description: "Scan-Fehler nur als Warnung melden (Exit 0) \u2014 gestufter Rollout"
|
|
1913
|
+
}),
|
|
1914
|
+
_ts_metadata12("design:type", Function),
|
|
1915
|
+
_ts_metadata12("design:paramtypes", []),
|
|
1916
|
+
_ts_metadata12("design:returntype", Boolean)
|
|
1917
|
+
], DiscoveryScanCommand.prototype, "parseNonFatal", null);
|
|
1918
|
+
DiscoveryScanCommand = _ts_decorate13([
|
|
1919
|
+
(0, import_common13.Injectable)(),
|
|
1920
|
+
(0, import_nest_commander5.SubCommand)({
|
|
1921
|
+
name: "scan",
|
|
1922
|
+
description: "Discovery-Snapshot headless erzeugen + persistieren (Seed-Gate, #23)"
|
|
1923
|
+
}),
|
|
1924
|
+
_ts_param8(0, (0, import_common13.Optional)()),
|
|
1925
|
+
_ts_param8(0, (0, import_common13.Inject)(import_nest6.DiscoveryScanner)),
|
|
1926
|
+
_ts_param8(1, (0, import_common13.Optional)()),
|
|
1927
|
+
_ts_param8(1, (0, import_common13.Inject)(import_nest6.DISCOVERY_SNAPSHOT_PATH_TOKEN)),
|
|
1928
|
+
_ts_metadata12("design:type", Function),
|
|
1929
|
+
_ts_metadata12("design:paramtypes", [
|
|
1930
|
+
Object,
|
|
1931
|
+
Object
|
|
1932
|
+
])
|
|
1933
|
+
], DiscoveryScanCommand);
|
|
1934
|
+
var DiscoveryCommands = class extends import_nest_commander5.CommandRunner {
|
|
1935
|
+
static {
|
|
1936
|
+
__name(this, "DiscoveryCommands");
|
|
1937
|
+
}
|
|
1938
|
+
async run() {
|
|
1939
|
+
process.stderr.write("Bitte Sub-Command angeben: scan.\n");
|
|
1940
|
+
process.exit(2);
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
DiscoveryCommands = _ts_decorate13([
|
|
1944
|
+
(0, import_common13.Injectable)(),
|
|
1945
|
+
(0, import_nest_commander5.Command)({
|
|
1946
|
+
name: "discovery",
|
|
1947
|
+
description: "Discovery-Operations (scan)",
|
|
1948
|
+
subCommands: [
|
|
1949
|
+
DiscoveryScanCommand
|
|
1950
|
+
]
|
|
1951
|
+
})
|
|
1952
|
+
], DiscoveryCommands);
|
|
1953
|
+
|
|
1954
|
+
// src/user.command.ts
|
|
1955
|
+
var import_common14 = require("@nestjs/common");
|
|
1956
|
+
var import_node_crypto = require("crypto");
|
|
1957
|
+
var import_nest_commander6 = require("nest-commander");
|
|
1958
|
+
function _ts_decorate14(decorators, target, key, desc) {
|
|
1959
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1960
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1961
|
+
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;
|
|
1962
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1963
|
+
}
|
|
1964
|
+
__name(_ts_decorate14, "_ts_decorate");
|
|
1965
|
+
function _ts_metadata13(k, v) {
|
|
1966
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1967
|
+
}
|
|
1968
|
+
__name(_ts_metadata13, "_ts_metadata");
|
|
1969
|
+
function _ts_param9(paramIndex, decorator) {
|
|
1970
|
+
return function(target, key) {
|
|
1971
|
+
decorator(target, key, paramIndex);
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
__name(_ts_param9, "_ts_param");
|
|
1975
|
+
var AUDIT_ENTITY = "User";
|
|
1976
|
+
function generatePassword() {
|
|
1977
|
+
return (0, import_node_crypto.randomBytes)(12).toString("base64url");
|
|
1978
|
+
}
|
|
1979
|
+
__name(generatePassword, "generatePassword");
|
|
1980
|
+
var UserCommands = class extends import_nest_commander6.CommandRunner {
|
|
1981
|
+
static {
|
|
1982
|
+
__name(this, "UserCommands");
|
|
1983
|
+
}
|
|
1984
|
+
ctx;
|
|
1985
|
+
users;
|
|
1986
|
+
constructor(ctx, users) {
|
|
1987
|
+
super(), this.ctx = ctx, this.users = users;
|
|
1988
|
+
}
|
|
1989
|
+
async run(args, flags) {
|
|
1990
|
+
const sub = args[0];
|
|
1991
|
+
const identity = this.ctx.resolveIdentity(flags.as);
|
|
1992
|
+
const me = await this.ctx.ensureSuperAdmin(identity);
|
|
1993
|
+
switch (sub) {
|
|
1994
|
+
case "create-super-admin":
|
|
1995
|
+
return this.createSuperAdmin(args[1], flags, identity, me.id, me.email);
|
|
1996
|
+
case "reassign-admin":
|
|
1997
|
+
return this.reassignAdmin(args[1], flags, identity, me.id);
|
|
1998
|
+
case "list":
|
|
1999
|
+
return this.list(args[1]);
|
|
2000
|
+
case "reset-password":
|
|
2001
|
+
return this.resetPassword(args[1], flags, identity, me.id);
|
|
2002
|
+
case "deactivate":
|
|
2003
|
+
return this.deactivate(args[1], flags, identity, me.id);
|
|
2004
|
+
default:
|
|
2005
|
+
throw new CliError("UNKNOWN_SUBCOMMAND", `Unbekannter Subbefehl: user ${sub ?? "(leer)"}. Verf\xFCgbar: create-super-admin <email>, reassign-admin <slug>, list <slug>, reset-password <email>, deactivate <email>.`, 1);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
async createSuperAdmin(email, flags, identity, meId, meEmail) {
|
|
2009
|
+
if (!email) {
|
|
2010
|
+
throw new CliError("MISSING_ARG", "user create-super-admin <email> erwartet eine E-Mail.", 1);
|
|
2011
|
+
}
|
|
2012
|
+
await this.ctx.requireMfa(meId);
|
|
2013
|
+
await this.ctx.ensureProductionConfirmation({
|
|
2014
|
+
yes: flags.yes
|
|
2015
|
+
});
|
|
2016
|
+
const generated = !flags.password;
|
|
2017
|
+
const password = flags.password ?? generatePassword();
|
|
2018
|
+
const created = await this.users.createSuperAdmin({
|
|
2019
|
+
email: email.toLowerCase(),
|
|
2020
|
+
password,
|
|
2021
|
+
firstName: flags.first,
|
|
2022
|
+
lastName: flags.last
|
|
2023
|
+
});
|
|
2024
|
+
await this.ctx.log({
|
|
2025
|
+
identity,
|
|
2026
|
+
userId: meId,
|
|
2027
|
+
entity: AUDIT_ENTITY,
|
|
2028
|
+
entityId: created.id,
|
|
2029
|
+
action: "SUPER_ADMIN_CREATE",
|
|
2030
|
+
changes: {
|
|
2031
|
+
email: created.email,
|
|
2032
|
+
createdBy: meEmail
|
|
2033
|
+
}
|
|
2034
|
+
});
|
|
2035
|
+
console.log(`\u2714 SUPER_ADMIN ${created.email} angelegt (durch ${meEmail}).`);
|
|
2036
|
+
console.log(` User-ID: ${created.id}`);
|
|
2037
|
+
if (generated) {
|
|
2038
|
+
console.log(` Passwort: ${password}`);
|
|
2039
|
+
console.log(" \u2192 Sicher \xFCbermitteln. Beim ersten Login \xE4ndern.");
|
|
2040
|
+
}
|
|
2041
|
+
console.log(` N\xE4chster Schritt: admin mfa-setup f\xFCr ${created.email}.`);
|
|
2042
|
+
}
|
|
2043
|
+
async reassignAdmin(slug, flags, identity, meId) {
|
|
2044
|
+
if (!slug) {
|
|
2045
|
+
throw new CliError("MISSING_ARG", "user reassign-admin <tenant-slug> erwartet einen Slug.", 1);
|
|
2046
|
+
}
|
|
2047
|
+
if (!flags.to) throw new CliError("MISSING_FLAG", "--to=<email> ist Pflicht.", 1);
|
|
2048
|
+
if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" ist Pflicht.', 1);
|
|
2049
|
+
await this.ctx.requireMfa(meId);
|
|
2050
|
+
const result = await this.users.reassignTenantAdmin(slug, flags.to.toLowerCase());
|
|
2051
|
+
await this.ctx.log({
|
|
2052
|
+
identity,
|
|
2053
|
+
userId: meId,
|
|
2054
|
+
entity: AUDIT_ENTITY,
|
|
2055
|
+
entityId: result.user.id,
|
|
2056
|
+
action: result.created ? "USER_REASSIGN_ADMIN" : "USER_ROLE_CHANGE",
|
|
2057
|
+
changes: {
|
|
2058
|
+
tenant: slug,
|
|
2059
|
+
to: "TENANT_ADMIN",
|
|
2060
|
+
from: result.previousRole,
|
|
2061
|
+
reason: flags.reason,
|
|
2062
|
+
emergency: true
|
|
2063
|
+
}
|
|
2064
|
+
});
|
|
2065
|
+
if (result.created) {
|
|
2066
|
+
console.log(`\u2714 Notfall-Admin ${result.user.email} f\xFCr ${slug} angelegt.`);
|
|
2067
|
+
if (result.oneTimePassword) {
|
|
2068
|
+
console.log(` Initial-Passwort: ${result.oneTimePassword}`);
|
|
2069
|
+
console.log(" \u2192 Sicher \xFCbermitteln. Beim ersten Login \xE4ndern.");
|
|
2070
|
+
}
|
|
2071
|
+
} else {
|
|
2072
|
+
console.log(`\u2714 ${result.user.email} ist jetzt TENANT_ADMIN von ${slug}.`);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
async list(slug) {
|
|
2076
|
+
if (!slug) {
|
|
2077
|
+
throw new CliError("MISSING_ARG", "user list <tenant-slug> erwartet einen Slug.", 1);
|
|
2078
|
+
}
|
|
2079
|
+
const rows = await this.users.listTenantUsers(slug);
|
|
2080
|
+
this.ctx.table(rows.map((u) => ({
|
|
2081
|
+
email: u.email,
|
|
2082
|
+
role: u.role,
|
|
2083
|
+
status: u.status,
|
|
2084
|
+
lastLogin: u.lastLoginAt?.slice(0, 10) ?? "\u2014"
|
|
2085
|
+
})));
|
|
2086
|
+
}
|
|
2087
|
+
async resetPassword(email, flags, identity, meId) {
|
|
2088
|
+
if (!email) {
|
|
2089
|
+
throw new CliError("MISSING_ARG", "user reset-password <email> erwartet eine E-Mail.", 1);
|
|
2090
|
+
}
|
|
2091
|
+
if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" ist Pflicht.', 1);
|
|
2092
|
+
const result = await this.users.triggerPasswordReset(email.toLowerCase());
|
|
2093
|
+
await this.ctx.log({
|
|
2094
|
+
identity,
|
|
2095
|
+
userId: meId,
|
|
2096
|
+
entity: AUDIT_ENTITY,
|
|
2097
|
+
entityId: result.user.id,
|
|
2098
|
+
action: "USER_PASSWORD_RESET_TRIGGERED",
|
|
2099
|
+
changes: {
|
|
2100
|
+
reason: flags.reason
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
if (result.oneTimePassword) {
|
|
2104
|
+
console.log(`\u2714 Einmal-Passwort f\xFCr ${result.user.email} gesetzt.`);
|
|
2105
|
+
console.log(` Passwort: ${result.oneTimePassword}`);
|
|
2106
|
+
console.log(" \u2192 Sicher \xFCbermitteln. Beim ersten Login \xE4ndern.");
|
|
2107
|
+
} else {
|
|
2108
|
+
console.log(`\u2714 Passwort-Reset f\xFCr ${result.user.email} ausgel\xF6st.`);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
async deactivate(email, flags, identity, meId) {
|
|
2112
|
+
if (!email) {
|
|
2113
|
+
throw new CliError("MISSING_ARG", "user deactivate <email> erwartet eine E-Mail.", 1);
|
|
2114
|
+
}
|
|
2115
|
+
if (!flags.reason) throw new CliError("MISSING_FLAG", '--reason="\u2026" ist Pflicht.', 1);
|
|
2116
|
+
await this.ctx.requireMfa(meId);
|
|
2117
|
+
await this.ctx.ensureProductionConfirmation({
|
|
2118
|
+
yes: flags.yes
|
|
2119
|
+
});
|
|
2120
|
+
const user = await this.users.deactivate(email.toLowerCase(), flags.reason);
|
|
2121
|
+
await this.ctx.log({
|
|
2122
|
+
identity,
|
|
2123
|
+
userId: meId,
|
|
2124
|
+
entity: AUDIT_ENTITY,
|
|
2125
|
+
entityId: user.id,
|
|
2126
|
+
action: "USER_DEACTIVATED",
|
|
2127
|
+
changes: {
|
|
2128
|
+
reason: flags.reason,
|
|
2129
|
+
emergency: true
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
console.log(`\u2714 ${user.email} deaktiviert.`);
|
|
2133
|
+
}
|
|
2134
|
+
parseAs(val) {
|
|
2135
|
+
return val;
|
|
2136
|
+
}
|
|
2137
|
+
parseTo(val) {
|
|
2138
|
+
return val;
|
|
2139
|
+
}
|
|
2140
|
+
parseReason(val) {
|
|
2141
|
+
return val;
|
|
2142
|
+
}
|
|
2143
|
+
parseYes() {
|
|
2144
|
+
return true;
|
|
2145
|
+
}
|
|
2146
|
+
parseFirst(val) {
|
|
2147
|
+
return val;
|
|
2148
|
+
}
|
|
2149
|
+
parseLast(val) {
|
|
2150
|
+
return val;
|
|
2151
|
+
}
|
|
2152
|
+
parsePassword(val) {
|
|
2153
|
+
return val;
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
_ts_decorate14([
|
|
2157
|
+
(0, import_nest_commander6.Option)({
|
|
2158
|
+
flags: "--as <email>",
|
|
2159
|
+
description: "CLI-Identit\xE4t (sonst <APP>_ADMIN_EMAIL)"
|
|
2160
|
+
}),
|
|
2161
|
+
_ts_metadata13("design:type", Function),
|
|
2162
|
+
_ts_metadata13("design:paramtypes", [
|
|
2163
|
+
String
|
|
2164
|
+
]),
|
|
2165
|
+
_ts_metadata13("design:returntype", String)
|
|
2166
|
+
], UserCommands.prototype, "parseAs", null);
|
|
2167
|
+
_ts_decorate14([
|
|
2168
|
+
(0, import_nest_commander6.Option)({
|
|
2169
|
+
flags: "--to <email>",
|
|
2170
|
+
description: "Ziel-User (reassign-admin)"
|
|
2171
|
+
}),
|
|
2172
|
+
_ts_metadata13("design:type", Function),
|
|
2173
|
+
_ts_metadata13("design:paramtypes", [
|
|
2174
|
+
String
|
|
2175
|
+
]),
|
|
2176
|
+
_ts_metadata13("design:returntype", String)
|
|
2177
|
+
], UserCommands.prototype, "parseTo", null);
|
|
2178
|
+
_ts_decorate14([
|
|
2179
|
+
(0, import_nest_commander6.Option)({
|
|
2180
|
+
flags: "--reason <text>",
|
|
2181
|
+
description: "Begr\xFCndung (Audit)"
|
|
2182
|
+
}),
|
|
2183
|
+
_ts_metadata13("design:type", Function),
|
|
2184
|
+
_ts_metadata13("design:paramtypes", [
|
|
2185
|
+
String
|
|
2186
|
+
]),
|
|
2187
|
+
_ts_metadata13("design:returntype", String)
|
|
2188
|
+
], UserCommands.prototype, "parseReason", null);
|
|
2189
|
+
_ts_decorate14([
|
|
2190
|
+
(0, import_nest_commander6.Option)({
|
|
2191
|
+
flags: "-y, --yes",
|
|
2192
|
+
description: "Production-Confirmation \xFCberspringen"
|
|
2193
|
+
}),
|
|
2194
|
+
_ts_metadata13("design:type", Function),
|
|
2195
|
+
_ts_metadata13("design:paramtypes", []),
|
|
2196
|
+
_ts_metadata13("design:returntype", Boolean)
|
|
2197
|
+
], UserCommands.prototype, "parseYes", null);
|
|
2198
|
+
_ts_decorate14([
|
|
2199
|
+
(0, import_nest_commander6.Option)({
|
|
2200
|
+
flags: "--first <name>",
|
|
2201
|
+
description: "Vorname (create-super-admin)"
|
|
2202
|
+
}),
|
|
2203
|
+
_ts_metadata13("design:type", Function),
|
|
2204
|
+
_ts_metadata13("design:paramtypes", [
|
|
2205
|
+
String
|
|
2206
|
+
]),
|
|
2207
|
+
_ts_metadata13("design:returntype", String)
|
|
2208
|
+
], UserCommands.prototype, "parseFirst", null);
|
|
2209
|
+
_ts_decorate14([
|
|
2210
|
+
(0, import_nest_commander6.Option)({
|
|
2211
|
+
flags: "--last <name>",
|
|
2212
|
+
description: "Nachname (create-super-admin)"
|
|
2213
|
+
}),
|
|
2214
|
+
_ts_metadata13("design:type", Function),
|
|
2215
|
+
_ts_metadata13("design:paramtypes", [
|
|
2216
|
+
String
|
|
2217
|
+
]),
|
|
2218
|
+
_ts_metadata13("design:returntype", String)
|
|
2219
|
+
], UserCommands.prototype, "parseLast", null);
|
|
2220
|
+
_ts_decorate14([
|
|
2221
|
+
(0, import_nest_commander6.Option)({
|
|
2222
|
+
flags: "--password <pwd>",
|
|
2223
|
+
description: "Passwort (create-super-admin; ohne Angabe generiert)"
|
|
2224
|
+
}),
|
|
2225
|
+
_ts_metadata13("design:type", Function),
|
|
2226
|
+
_ts_metadata13("design:paramtypes", [
|
|
2227
|
+
String
|
|
2228
|
+
]),
|
|
2229
|
+
_ts_metadata13("design:returntype", String)
|
|
2230
|
+
], UserCommands.prototype, "parsePassword", null);
|
|
2231
|
+
UserCommands = _ts_decorate14([
|
|
2232
|
+
(0, import_common14.Injectable)(),
|
|
2233
|
+
(0, import_nest_commander6.Command)({
|
|
2234
|
+
name: "user",
|
|
2235
|
+
description: "User-Operationen (create-super-admin, reassign-admin, list, reset-password, deactivate)"
|
|
2236
|
+
}),
|
|
2237
|
+
_ts_param9(1, (0, import_common14.Inject)(USER_MANAGEMENT_PORT_TOKEN)),
|
|
2238
|
+
_ts_metadata13("design:type", Function),
|
|
2239
|
+
_ts_metadata13("design:paramtypes", [
|
|
2240
|
+
typeof CliContextService === "undefined" ? Object : CliContextService,
|
|
2241
|
+
typeof UserManagementPort === "undefined" ? Object : UserManagementPort
|
|
2242
|
+
])
|
|
2243
|
+
], UserCommands);
|
|
2244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2245
|
+
0 && (module.exports = {
|
|
2246
|
+
AUDIT_QUERY_PORT_TOKEN,
|
|
2247
|
+
AdminCommands,
|
|
2248
|
+
AdminManifestDoctorCheck,
|
|
2249
|
+
AdminMfaSetupCommand,
|
|
2250
|
+
AdminWhoamiCommand,
|
|
2251
|
+
AuditCommands,
|
|
2252
|
+
AuditTailCommand,
|
|
2253
|
+
AuditTailFlow,
|
|
2254
|
+
CLI_CONTEXT_CONFIG_TOKEN,
|
|
2255
|
+
CliContextModule,
|
|
2256
|
+
CliContextService,
|
|
2257
|
+
CliError,
|
|
2258
|
+
DEFAULT_MANIFEST_CHECKS,
|
|
2259
|
+
DOCTOR_CHECKS_TOKEN,
|
|
2260
|
+
DiscoveryCommands,
|
|
2261
|
+
DiscoveryScanCommand,
|
|
2262
|
+
DiscoverySnapshotDoctorCheck,
|
|
2263
|
+
DoctorCommands,
|
|
2264
|
+
DoctorFlow,
|
|
2265
|
+
MANIFEST_ACCESS_PORT_TOKEN,
|
|
2266
|
+
MANIFEST_CHECKS_TOKEN,
|
|
2267
|
+
ManifestCheckCommand,
|
|
2268
|
+
ManifestCliFlow,
|
|
2269
|
+
ManifestCommands,
|
|
2270
|
+
ManifestDumpCommand,
|
|
2271
|
+
ManifestHashCommand,
|
|
2272
|
+
ManifestValidateCommand,
|
|
2273
|
+
MfaSetupFlow,
|
|
2274
|
+
PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
2275
|
+
PlanCatalogDoctorCheck,
|
|
2276
|
+
USER_MANAGEMENT_PORT_TOKEN,
|
|
2277
|
+
USER_PORT_TOKEN,
|
|
2278
|
+
UserCommands,
|
|
2279
|
+
UserPortDoctorCheck,
|
|
2280
|
+
WhoAmIFlow,
|
|
2281
|
+
applyFragmentBlocks,
|
|
2282
|
+
extractModelBlocks,
|
|
2283
|
+
extractModelNames
|
|
2284
|
+
});
|