@zintrust/core 0.4.75 → 0.4.77
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/bin/zintrust-main.d.ts +12 -0
- package/bin/zintrust-main.d.ts.map +1 -1
- package/bin/zintrust-main.js +149 -54
- package/package.json +1 -1
- package/src/boot/registry/runtime.d.ts.map +1 -1
- package/src/boot/registry/runtime.js +60 -25
- package/src/cache/Cache.d.ts.map +1 -1
- package/src/cache/Cache.js +11 -11
- package/src/cli/BaseCommand.js +3 -3
- package/src/cli/OptionalCliExtensions.js +8 -8
- package/src/cli/commands/MigrateCommand.js +1 -1
- package/src/cli/commands/TraceCommand.d.ts +18 -0
- package/src/cli/commands/TraceCommand.d.ts.map +1 -0
- package/src/cli/commands/TraceCommand.js +6 -0
- package/src/cli/commands/TraceCommands.d.ts +25 -0
- package/src/cli/commands/TraceCommands.d.ts.map +1 -0
- package/src/cli/commands/{DebuggerCommands.js → TraceCommands.js} +112 -120
- package/src/cli/commands/index.d.ts +1 -1
- package/src/cli/commands/index.d.ts.map +1 -1
- package/src/cli/commands/index.js +1 -1
- package/src/cli/index.d.ts +2 -2
- package/src/cli/index.d.ts.map +1 -1
- package/src/cli/index.js +2 -2
- package/src/cli.d.ts +1 -1
- package/src/cli.d.ts.map +1 -1
- package/src/cli.js +1 -1
- package/src/config/logging/KvLogger.d.ts.map +1 -1
- package/src/config/logging/KvLogger.js +18 -6
- package/src/events/EventDispatcher.js +3 -3
- package/src/functions/cloudflare.d.ts.map +1 -1
- package/src/functions/cloudflare.js +8 -0
- package/src/http/RequestContext.d.ts.map +1 -1
- package/src/http/RequestContext.js +5 -6
- package/src/index.d.ts +2 -1
- package/src/index.d.ts.map +1 -1
- package/src/index.js +5 -4
- package/src/middleware/ErrorHandlerMiddleware.d.ts.map +1 -1
- package/src/middleware/ErrorHandlerMiddleware.js +2 -0
- package/src/orm/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLiteAdapter.js +2 -1
- package/src/orm/migrations/MigrationStore.d.ts.map +1 -1
- package/src/orm/migrations/MigrationStore.js +352 -16
- package/src/runtime/StartupConfigFileRegistry.d.ts +2 -1
- package/src/runtime/StartupConfigFileRegistry.d.ts.map +1 -1
- package/src/runtime/StartupConfigFileRegistry.js +1 -0
- package/src/runtime/plugins/trace-runtime.d.ts +24 -0
- package/src/runtime/plugins/trace-runtime.d.ts.map +1 -0
- package/src/runtime/plugins/trace-runtime.js +63 -0
- package/src/runtime/plugins/trace.d.ts +2 -0
- package/src/runtime/plugins/trace.d.ts.map +1 -0
- package/src/runtime/plugins/{system-debugger.js → trace.js} +2 -2
- package/src/security/JwtManager.js +4 -4
- package/src/templates/project/basic/tsconfig.json.tpl +11 -12
- package/src/tools/http/Http.js +2 -2
- package/src/tools/mail/index.js +2 -2
- package/src/tools/notification/Service.js +6 -6
- package/src/tools/queue/Queue.d.ts +1 -2
- package/src/tools/queue/Queue.d.ts.map +1 -1
- package/src/tools/queue/Queue.js +3 -4
- package/src/{debugger/SystemDebuggerBridge.d.ts → trace/SystemTraceBridge.d.ts} +3 -3
- package/src/trace/SystemTraceBridge.d.ts.map +1 -0
- package/src/{debugger/SystemDebuggerBridge.js → trace/SystemTraceBridge.js} +20 -20
- package/src/zintrust.comon.d.ts +11 -0
- package/src/zintrust.comon.d.ts.map +1 -0
- package/src/zintrust.comon.js +17 -0
- package/src/zintrust.plugins.d.ts +7 -3
- package/src/zintrust.plugins.d.ts.map +1 -1
- package/src/zintrust.plugins.js +9 -3
- package/src/zintrust.plugins.wg.d.ts +1 -0
- package/src/zintrust.plugins.wg.d.ts.map +1 -1
- package/src/zintrust.plugins.wg.js +3 -0
- package/src/cli/commands/DebuggerCommand.d.ts +0 -18
- package/src/cli/commands/DebuggerCommand.d.ts.map +0 -1
- package/src/cli/commands/DebuggerCommand.js +0 -6
- package/src/cli/commands/DebuggerCommands.d.ts +0 -25
- package/src/cli/commands/DebuggerCommands.d.ts.map +0 -1
- package/src/debugger/SystemDebuggerBridge.d.ts.map +0 -1
- package/src/runtime/plugins/system-debugger-runtime.d.ts +0 -19
- package/src/runtime/plugins/system-debugger-runtime.d.ts.map +0 -1
- package/src/runtime/plugins/system-debugger-runtime.js +0 -19
- package/src/runtime/plugins/system-debugger.d.ts +0 -2
- package/src/runtime/plugins/system-debugger.d.ts.map +0 -1
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { Env } from '../../config/env.js';
|
|
2
2
|
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
3
3
|
import { QueryBuilder } from '../QueryBuilder.js';
|
|
4
|
+
const DEFAULT_LAYOUT = Object.freeze({
|
|
5
|
+
hasAppliedAt: true,
|
|
6
|
+
hasCreatedAt: true,
|
|
7
|
+
hasMigration: false,
|
|
8
|
+
hasName: true,
|
|
9
|
+
hasScope: true,
|
|
10
|
+
hasService: true,
|
|
11
|
+
hasStatus: true,
|
|
12
|
+
requiresCompatibilityMode: false,
|
|
13
|
+
});
|
|
14
|
+
const tableLayoutCache = new WeakMap();
|
|
4
15
|
function nowIso() {
|
|
5
16
|
// MySQL/MariaDB DATETIME does not accept ISO8601 with timezone (e.g. trailing 'Z').
|
|
6
17
|
// Use a portable UTC datetime string.
|
|
@@ -11,6 +22,230 @@ const toSafeService = (service) => {
|
|
|
11
22
|
return '';
|
|
12
23
|
return service.length > 0 ? service : '';
|
|
13
24
|
};
|
|
25
|
+
const isDefaultTrackingTarget = (scope, service) => {
|
|
26
|
+
return scope === 'global' && service === '';
|
|
27
|
+
};
|
|
28
|
+
const queryExists = async (db, sql, parameters) => {
|
|
29
|
+
const rows = await db.query(sql, parameters, true);
|
|
30
|
+
return rows.length > 0;
|
|
31
|
+
};
|
|
32
|
+
const schemaHasTable = async (db, tableName) => {
|
|
33
|
+
const driver = db.getType();
|
|
34
|
+
if (driver === 'sqlite' || driver === 'd1' || driver === 'd1-remote') {
|
|
35
|
+
return queryExists(db, "SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1", [tableName]);
|
|
36
|
+
}
|
|
37
|
+
if (driver === 'postgresql') {
|
|
38
|
+
return queryExists(db, "SELECT 1 FROM information_schema.tables WHERE table_schema='public' AND table_name=? LIMIT 1", [tableName]);
|
|
39
|
+
}
|
|
40
|
+
if (driver === 'mysql') {
|
|
41
|
+
return queryExists(db, 'SELECT 1 FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name=? LIMIT 1', [tableName]);
|
|
42
|
+
}
|
|
43
|
+
if (driver === 'sqlserver') {
|
|
44
|
+
return queryExists(db, 'SELECT 1 FROM sys.tables WHERE name=? LIMIT 1', [tableName]);
|
|
45
|
+
}
|
|
46
|
+
throw ErrorFactory.createCliError(`Unsupported DB driver: ${driver}`);
|
|
47
|
+
};
|
|
48
|
+
const schemaHasColumn = async (db, tableName, columnName) => {
|
|
49
|
+
const driver = db.getType();
|
|
50
|
+
if (driver === 'sqlite' || driver === 'd1' || driver === 'd1-remote') {
|
|
51
|
+
const rows = await db.query(`PRAGMA table_info("${tableName}")`, [], true);
|
|
52
|
+
return rows.some((row) => {
|
|
53
|
+
const record = row;
|
|
54
|
+
return typeof record.name === 'string' && record.name === columnName;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (driver === 'postgresql') {
|
|
58
|
+
return queryExists(db, "SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name=? AND column_name=? LIMIT 1", [tableName, columnName]);
|
|
59
|
+
}
|
|
60
|
+
if (driver === 'mysql') {
|
|
61
|
+
return queryExists(db, 'SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name=? AND column_name=? LIMIT 1', [tableName, columnName]);
|
|
62
|
+
}
|
|
63
|
+
if (driver === 'sqlserver') {
|
|
64
|
+
return queryExists(db, 'SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID(?) AND name=?', [
|
|
65
|
+
tableName,
|
|
66
|
+
columnName,
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
throw ErrorFactory.createCliError(`Unsupported DB driver: ${driver}`);
|
|
70
|
+
};
|
|
71
|
+
const clearTableLayoutCache = (db) => {
|
|
72
|
+
tableLayoutCache.delete(db);
|
|
73
|
+
};
|
|
74
|
+
const ensureTrackingTable = async (db) => {
|
|
75
|
+
assertDbSupportsMigrations(db);
|
|
76
|
+
const adapter = db.getAdapterInstance(false);
|
|
77
|
+
const ensure = requireMigrationsTableSupport(adapter);
|
|
78
|
+
// getAdapterInstance(false) returns a raw adapter without going through Database.query()
|
|
79
|
+
// which auto-connects; ensure we're connected before creating the migrations table.
|
|
80
|
+
if (typeof db.connect === 'function') {
|
|
81
|
+
await db.connect();
|
|
82
|
+
}
|
|
83
|
+
else if (typeof adapter.connect === 'function') {
|
|
84
|
+
await adapter.connect();
|
|
85
|
+
}
|
|
86
|
+
clearTableLayoutCache(db);
|
|
87
|
+
await ensure();
|
|
88
|
+
clearTableLayoutCache(db);
|
|
89
|
+
};
|
|
90
|
+
const resolveTableLayout = async (db, allowEnsure = true) => {
|
|
91
|
+
const cached = tableLayoutCache.get(db);
|
|
92
|
+
if (cached !== undefined)
|
|
93
|
+
return cached;
|
|
94
|
+
const layoutPromise = (async () => {
|
|
95
|
+
if (typeof db.query !== 'function')
|
|
96
|
+
return DEFAULT_LAYOUT;
|
|
97
|
+
const [hasTable, hasName, hasMigration, hasScope, hasService, hasStatus, hasAppliedAt, hasCreatedAt] = await Promise.all([
|
|
98
|
+
schemaHasTable(db, 'migrations'),
|
|
99
|
+
schemaHasColumn(db, 'migrations', 'name'),
|
|
100
|
+
schemaHasColumn(db, 'migrations', 'migration'),
|
|
101
|
+
schemaHasColumn(db, 'migrations', 'scope'),
|
|
102
|
+
schemaHasColumn(db, 'migrations', 'service'),
|
|
103
|
+
schemaHasColumn(db, 'migrations', 'status'),
|
|
104
|
+
schemaHasColumn(db, 'migrations', 'applied_at'),
|
|
105
|
+
schemaHasColumn(db, 'migrations', 'created_at'),
|
|
106
|
+
]);
|
|
107
|
+
if (!hasTable && allowEnsure) {
|
|
108
|
+
await ensureTrackingTable(db);
|
|
109
|
+
return resolveTableLayout(db, false);
|
|
110
|
+
}
|
|
111
|
+
if (!hasName && !hasMigration) {
|
|
112
|
+
throw ErrorFactory.createCliError('The migrations table is missing both `name` and `migration` columns. Update the tracking table before running migrations.');
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
hasAppliedAt,
|
|
116
|
+
hasCreatedAt,
|
|
117
|
+
hasMigration,
|
|
118
|
+
hasName,
|
|
119
|
+
hasScope,
|
|
120
|
+
hasService,
|
|
121
|
+
hasStatus,
|
|
122
|
+
requiresCompatibilityMode: hasMigration || !hasName || !hasScope || !hasService || !hasStatus,
|
|
123
|
+
};
|
|
124
|
+
})();
|
|
125
|
+
tableLayoutCache.set(db, layoutPromise);
|
|
126
|
+
return layoutPromise.catch((error) => {
|
|
127
|
+
if (tableLayoutCache.get(db) === layoutPromise) {
|
|
128
|
+
clearTableLayoutCache(db);
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
const assertCompatibleTrackingTarget = (layout, scope, service) => {
|
|
134
|
+
if ((layout.hasScope && layout.hasService) || isDefaultTrackingTarget(scope, service))
|
|
135
|
+
return;
|
|
136
|
+
throw ErrorFactory.createCliError('The existing migrations table uses the legacy schema and does not support scoped/service-specific migration tracking yet.');
|
|
137
|
+
};
|
|
138
|
+
const buildLegacyIdentity = (layout, name, scope, service) => {
|
|
139
|
+
const conditions = [];
|
|
140
|
+
const params = [];
|
|
141
|
+
if (layout.hasName && layout.hasMigration) {
|
|
142
|
+
conditions.push('(name = ? OR migration = ?)');
|
|
143
|
+
params.push(name, name);
|
|
144
|
+
}
|
|
145
|
+
else if (layout.hasName) {
|
|
146
|
+
conditions.push('name = ?');
|
|
147
|
+
params.push(name);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
conditions.push('migration = ?');
|
|
151
|
+
params.push(name);
|
|
152
|
+
}
|
|
153
|
+
if (layout.hasScope) {
|
|
154
|
+
conditions.push('scope = ?');
|
|
155
|
+
params.push(scope);
|
|
156
|
+
}
|
|
157
|
+
if (layout.hasService) {
|
|
158
|
+
conditions.push('service = ?');
|
|
159
|
+
params.push(service);
|
|
160
|
+
}
|
|
161
|
+
return { params, sql: conditions.join(' AND ') };
|
|
162
|
+
};
|
|
163
|
+
const normalizeLegacyName = (row) => {
|
|
164
|
+
if (typeof row['name'] === 'string' && row['name'].length > 0)
|
|
165
|
+
return row['name'];
|
|
166
|
+
if (typeof row['migration'] === 'string' && row['migration'].length > 0)
|
|
167
|
+
return row['migration'];
|
|
168
|
+
return '';
|
|
169
|
+
};
|
|
170
|
+
const getLegacyBatch = (row) => {
|
|
171
|
+
const value = row['batch'];
|
|
172
|
+
return typeof value === 'number' ? value : Number(value);
|
|
173
|
+
};
|
|
174
|
+
const getLegacyAppliedAt = (row) => {
|
|
175
|
+
return typeof row['applied_at'] === 'string' ? row['applied_at'] : null;
|
|
176
|
+
};
|
|
177
|
+
const getLegacyAppliedRows = async (db, layout, scope, service) => {
|
|
178
|
+
const normalizedService = toSafeService(service);
|
|
179
|
+
assertCompatibleTrackingTarget(layout, scope, normalizedService);
|
|
180
|
+
const selectColumns = [
|
|
181
|
+
...(layout.hasName ? ['name'] : []),
|
|
182
|
+
...(layout.hasMigration ? ['migration'] : []),
|
|
183
|
+
'batch',
|
|
184
|
+
...(layout.hasAppliedAt ? ['applied_at'] : []),
|
|
185
|
+
];
|
|
186
|
+
const conditions = [];
|
|
187
|
+
const params = [];
|
|
188
|
+
if (layout.hasStatus) {
|
|
189
|
+
conditions.push('status = ?');
|
|
190
|
+
params.push('completed');
|
|
191
|
+
}
|
|
192
|
+
if (layout.hasScope) {
|
|
193
|
+
conditions.push('scope = ?');
|
|
194
|
+
params.push(scope);
|
|
195
|
+
}
|
|
196
|
+
if (layout.hasService) {
|
|
197
|
+
conditions.push('service = ?');
|
|
198
|
+
params.push(normalizedService);
|
|
199
|
+
}
|
|
200
|
+
const whereClause = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '';
|
|
201
|
+
return (await db.query(`SELECT ${selectColumns.join(', ')} FROM migrations${whereClause}`, params, true));
|
|
202
|
+
};
|
|
203
|
+
const findLegacyMigrationRecord = async (db, layout, params) => {
|
|
204
|
+
const identity = buildLegacyIdentity(layout, params.name, params.scope, params.service);
|
|
205
|
+
const existing = (await db.query(`SELECT id FROM migrations WHERE ${identity.sql} LIMIT 1`, identity.params, true));
|
|
206
|
+
return existing.length > 0;
|
|
207
|
+
};
|
|
208
|
+
const updateLegacyRunningRecord = async (db, layout, params) => {
|
|
209
|
+
const identity = buildLegacyIdentity(layout, params.name, params.scope, params.service);
|
|
210
|
+
const assignments = ['batch = ?'];
|
|
211
|
+
const updateParams = [params.batch];
|
|
212
|
+
if (layout.hasAppliedAt) {
|
|
213
|
+
assignments.push('applied_at = ?');
|
|
214
|
+
updateParams.push(null);
|
|
215
|
+
}
|
|
216
|
+
await db.execute(`UPDATE migrations SET ${assignments.join(', ')} WHERE ${identity.sql}`, [
|
|
217
|
+
...updateParams,
|
|
218
|
+
...identity.params,
|
|
219
|
+
]);
|
|
220
|
+
};
|
|
221
|
+
const buildLegacyInsertRunningPayload = (layout, params) => {
|
|
222
|
+
return {
|
|
223
|
+
columns: [
|
|
224
|
+
...(layout.hasName ? ['name'] : []),
|
|
225
|
+
...(layout.hasMigration ? ['migration'] : []),
|
|
226
|
+
...(layout.hasScope ? ['scope'] : []),
|
|
227
|
+
...(layout.hasService ? ['service'] : []),
|
|
228
|
+
'batch',
|
|
229
|
+
...(layout.hasStatus ? ['status'] : []),
|
|
230
|
+
...(layout.hasAppliedAt ? ['applied_at'] : []),
|
|
231
|
+
...(layout.hasCreatedAt ? ['created_at'] : []),
|
|
232
|
+
],
|
|
233
|
+
values: [
|
|
234
|
+
...(layout.hasName ? [params.name] : []),
|
|
235
|
+
...(layout.hasMigration ? [params.name] : []),
|
|
236
|
+
...(layout.hasScope ? [params.scope] : []),
|
|
237
|
+
...(layout.hasService ? [params.service] : []),
|
|
238
|
+
params.batch,
|
|
239
|
+
...(layout.hasStatus ? ['running'] : []),
|
|
240
|
+
...(layout.hasAppliedAt ? [null] : []),
|
|
241
|
+
...(layout.hasCreatedAt ? [nowIso()] : []),
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
const insertLegacyRunningRecord = async (db, layout, params) => {
|
|
246
|
+
const payload = buildLegacyInsertRunningPayload(layout, params);
|
|
247
|
+
await db.execute(`INSERT INTO migrations (${payload.columns.join(', ')}) VALUES (${payload.columns.map(() => '?').join(', ')})`, payload.values);
|
|
248
|
+
};
|
|
14
249
|
const assertDbSupportsMigrations = (db) => {
|
|
15
250
|
const t = db.getType();
|
|
16
251
|
if (t === 'd1') {
|
|
@@ -42,22 +277,22 @@ const requireMigrationsTableSupport = (adapter) => {
|
|
|
42
277
|
};
|
|
43
278
|
export const MigrationStore = Object.freeze({
|
|
44
279
|
async ensureTable(db) {
|
|
45
|
-
|
|
46
|
-
const adapter = db.getAdapterInstance(false);
|
|
47
|
-
const ensure = requireMigrationsTableSupport(adapter);
|
|
48
|
-
// getAdapterInstance(false) returns a raw adapter without going through Database.query()
|
|
49
|
-
// which auto-connects; ensure we're connected before creating the migrations table.
|
|
50
|
-
if (typeof db.connect === 'function') {
|
|
51
|
-
await db.connect();
|
|
52
|
-
}
|
|
53
|
-
else if (typeof adapter.connect === 'function') {
|
|
54
|
-
await adapter.connect();
|
|
55
|
-
}
|
|
56
|
-
await ensure();
|
|
280
|
+
await ensureTrackingTable(db);
|
|
57
281
|
},
|
|
58
282
|
async getLastCompletedBatch(db, scope = 'global', service = '') {
|
|
59
283
|
assertDbSupportsMigrations(db);
|
|
284
|
+
const layout = await resolveTableLayout(db);
|
|
60
285
|
const normalizedService = toSafeService(service);
|
|
286
|
+
if (layout.requiresCompatibilityMode) {
|
|
287
|
+
const rows = await getLegacyAppliedRows(db, layout, scope, normalizedService);
|
|
288
|
+
let maxBatch = 0;
|
|
289
|
+
for (const row of rows) {
|
|
290
|
+
const batch = getLegacyBatch(row);
|
|
291
|
+
if (Number.isFinite(batch) && batch > maxBatch)
|
|
292
|
+
maxBatch = batch;
|
|
293
|
+
}
|
|
294
|
+
return maxBatch;
|
|
295
|
+
}
|
|
61
296
|
const row = await QueryBuilder.create('migrations', db)
|
|
62
297
|
.max('batch', 'max_batch')
|
|
63
298
|
.where('status', '=', 'completed')
|
|
@@ -70,6 +305,28 @@ export const MigrationStore = Object.freeze({
|
|
|
70
305
|
async getAppliedMap(db, scope, service) {
|
|
71
306
|
assertDbSupportsMigrations(db);
|
|
72
307
|
const normalizedService = toSafeService(service);
|
|
308
|
+
const layout = await resolveTableLayout(db);
|
|
309
|
+
if (layout.requiresCompatibilityMode) {
|
|
310
|
+
const rows = await getLegacyAppliedRows(db, layout, scope, normalizedService);
|
|
311
|
+
const map = new Map();
|
|
312
|
+
for (const row of rows) {
|
|
313
|
+
const name = normalizeLegacyName(row);
|
|
314
|
+
const batch = getLegacyBatch(row);
|
|
315
|
+
if (name === '' || !Number.isFinite(batch))
|
|
316
|
+
continue;
|
|
317
|
+
map.set(name, {
|
|
318
|
+
name,
|
|
319
|
+
scope: layout.hasScope ? scope : 'global',
|
|
320
|
+
service: layout.hasService ? normalizedService : '',
|
|
321
|
+
batch,
|
|
322
|
+
status: typeof row['status'] === 'string'
|
|
323
|
+
? row['status']
|
|
324
|
+
: 'completed',
|
|
325
|
+
appliedAt: getLegacyAppliedAt(row),
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
return map;
|
|
329
|
+
}
|
|
73
330
|
const rows = await QueryBuilder.create('migrations', db)
|
|
74
331
|
.select('name', 'scope', 'service', 'batch', 'status')
|
|
75
332
|
.selectAs('applied_at', 'appliedAt')
|
|
@@ -90,6 +347,22 @@ export const MigrationStore = Object.freeze({
|
|
|
90
347
|
async insertRunning(db, params) {
|
|
91
348
|
assertDbSupportsMigrations(db);
|
|
92
349
|
const normalizedService = toSafeService(params.service);
|
|
350
|
+
const layout = await resolveTableLayout(db);
|
|
351
|
+
if (layout.requiresCompatibilityMode) {
|
|
352
|
+
assertCompatibleTrackingTarget(layout, params.scope, normalizedService);
|
|
353
|
+
const legacyParams = {
|
|
354
|
+
name: params.name,
|
|
355
|
+
scope: params.scope,
|
|
356
|
+
service: normalizedService,
|
|
357
|
+
batch: params.batch,
|
|
358
|
+
};
|
|
359
|
+
if (await findLegacyMigrationRecord(db, layout, legacyParams)) {
|
|
360
|
+
await updateLegacyRunningRecord(db, layout, legacyParams);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
await insertLegacyRunningRecord(db, layout, legacyParams);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
93
366
|
const existing = await QueryBuilder.create('migrations', db)
|
|
94
367
|
.select('id')
|
|
95
368
|
.where('name', '=', params.name)
|
|
@@ -122,10 +395,37 @@ export const MigrationStore = Object.freeze({
|
|
|
122
395
|
},
|
|
123
396
|
async markStatus(db, params) {
|
|
124
397
|
assertDbSupportsMigrations(db);
|
|
398
|
+
const normalizedService = toSafeService(params.service);
|
|
399
|
+
const layout = await resolveTableLayout(db);
|
|
400
|
+
if (layout.requiresCompatibilityMode) {
|
|
401
|
+
assertCompatibleTrackingTarget(layout, params.scope, normalizedService);
|
|
402
|
+
const identity = buildLegacyIdentity(layout, params.name, params.scope, normalizedService);
|
|
403
|
+
if (!layout.hasStatus && params.status === 'failed') {
|
|
404
|
+
await db.execute(`DELETE FROM migrations WHERE ${identity.sql}`, identity.params);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const assignments = [];
|
|
408
|
+
const updateParams = [];
|
|
409
|
+
if (layout.hasStatus) {
|
|
410
|
+
assignments.push('status = ?');
|
|
411
|
+
updateParams.push(params.status);
|
|
412
|
+
}
|
|
413
|
+
if (params.appliedAt !== undefined && layout.hasAppliedAt) {
|
|
414
|
+
assignments.push('applied_at = ?');
|
|
415
|
+
updateParams.push(params.appliedAt);
|
|
416
|
+
}
|
|
417
|
+
if (assignments.length === 0)
|
|
418
|
+
return;
|
|
419
|
+
await db.execute(`UPDATE migrations SET ${assignments.join(', ')} WHERE ${identity.sql}`, [
|
|
420
|
+
...updateParams,
|
|
421
|
+
...identity.params,
|
|
422
|
+
]);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
125
425
|
const builder = QueryBuilder.create('migrations', db)
|
|
126
426
|
.where('name', '=', params.name)
|
|
127
427
|
.andWhere('scope', '=', params.scope)
|
|
128
|
-
.andWhere('service', '=',
|
|
428
|
+
.andWhere('service', '=', normalizedService);
|
|
129
429
|
if (params.appliedAt !== undefined) {
|
|
130
430
|
await builder.update({ status: params.status, applied_at: params.appliedAt });
|
|
131
431
|
return;
|
|
@@ -134,11 +434,30 @@ export const MigrationStore = Object.freeze({
|
|
|
134
434
|
},
|
|
135
435
|
async listCompletedInBatchesGte(db, params) {
|
|
136
436
|
assertDbSupportsMigrations(db);
|
|
437
|
+
const normalizedService = toSafeService(params.service);
|
|
438
|
+
const layout = await resolveTableLayout(db);
|
|
439
|
+
if (layout.requiresCompatibilityMode) {
|
|
440
|
+
const rows = await getLegacyAppliedRows(db, layout, params.scope, normalizedService);
|
|
441
|
+
const out = [];
|
|
442
|
+
for (const row of rows) {
|
|
443
|
+
const name = normalizeLegacyName(row);
|
|
444
|
+
const batch = getLegacyBatch(row);
|
|
445
|
+
if (name === '' || !Number.isFinite(batch) || batch < params.minBatch)
|
|
446
|
+
continue;
|
|
447
|
+
out.push({ name, batch });
|
|
448
|
+
}
|
|
449
|
+
out.sort((left, right) => {
|
|
450
|
+
if (left.batch !== right.batch)
|
|
451
|
+
return right.batch - left.batch;
|
|
452
|
+
return right.name.localeCompare(left.name);
|
|
453
|
+
});
|
|
454
|
+
return out;
|
|
455
|
+
}
|
|
137
456
|
const rows = await QueryBuilder.create('migrations', db)
|
|
138
457
|
.select('name', 'batch')
|
|
139
458
|
.where('status', '=', 'completed')
|
|
140
459
|
.andWhere('scope', '=', params.scope)
|
|
141
|
-
.andWhere('service', '=',
|
|
460
|
+
.andWhere('service', '=', normalizedService)
|
|
142
461
|
.andWhere('batch', '>=', params.minBatch)
|
|
143
462
|
.orderBy('batch', 'DESC')
|
|
144
463
|
.orderBy('id', 'DESC')
|
|
@@ -157,11 +476,20 @@ export const MigrationStore = Object.freeze({
|
|
|
157
476
|
},
|
|
158
477
|
async listAllCompletedNames(db, params) {
|
|
159
478
|
assertDbSupportsMigrations(db);
|
|
479
|
+
const normalizedService = toSafeService(params.service);
|
|
480
|
+
const layout = await resolveTableLayout(db);
|
|
481
|
+
if (layout.requiresCompatibilityMode) {
|
|
482
|
+
const rows = await getLegacyAppliedRows(db, layout, params.scope, normalizedService);
|
|
483
|
+
return rows
|
|
484
|
+
.map(normalizeLegacyName)
|
|
485
|
+
.filter((name) => name.length > 0)
|
|
486
|
+
.sort((left, right) => right.localeCompare(left));
|
|
487
|
+
}
|
|
160
488
|
const rows = await QueryBuilder.create('migrations', db)
|
|
161
489
|
.select('name')
|
|
162
490
|
.where('status', '=', 'completed')
|
|
163
491
|
.andWhere('scope', '=', params.scope)
|
|
164
|
-
.andWhere('service', '=',
|
|
492
|
+
.andWhere('service', '=', normalizedService)
|
|
165
493
|
.orderBy('batch', 'DESC')
|
|
166
494
|
.orderBy('id', 'DESC')
|
|
167
495
|
.get();
|
|
@@ -169,10 +497,18 @@ export const MigrationStore = Object.freeze({
|
|
|
169
497
|
},
|
|
170
498
|
async deleteRecord(db, params) {
|
|
171
499
|
assertDbSupportsMigrations(db);
|
|
500
|
+
const normalizedService = toSafeService(params.service);
|
|
501
|
+
const layout = await resolveTableLayout(db);
|
|
502
|
+
if (layout.requiresCompatibilityMode) {
|
|
503
|
+
assertCompatibleTrackingTarget(layout, params.scope, normalizedService);
|
|
504
|
+
const identity = buildLegacyIdentity(layout, params.name, params.scope, normalizedService);
|
|
505
|
+
await db.execute(`DELETE FROM migrations WHERE ${identity.sql}`, identity.params);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
172
508
|
await QueryBuilder.create('migrations', db)
|
|
173
509
|
.where('name', '=', params.name)
|
|
174
510
|
.andWhere('scope', '=', params.scope)
|
|
175
|
-
.andWhere('service', '=',
|
|
511
|
+
.andWhere('service', '=', normalizedService)
|
|
176
512
|
.delete();
|
|
177
513
|
},
|
|
178
514
|
});
|
|
@@ -3,13 +3,14 @@ export declare const StartupConfigFile: {
|
|
|
3
3
|
readonly Cache: "config/cache.ts";
|
|
4
4
|
readonly Database: "config/database.ts";
|
|
5
5
|
readonly Mail: "config/mail.ts";
|
|
6
|
+
readonly Trace: "config/trace.ts";
|
|
6
7
|
readonly Middleware: "config/middleware.ts";
|
|
7
8
|
readonly Notification: "config/notification.ts";
|
|
8
9
|
readonly Queue: "config/queue.ts";
|
|
9
10
|
readonly Storage: "config/storage.ts";
|
|
10
11
|
readonly Workers: "config/workers.ts";
|
|
11
12
|
};
|
|
12
|
-
export type StartupConfigFileTypes = typeof StartupConfigFile.Broadcast | typeof StartupConfigFile.Cache | typeof StartupConfigFile.Database | typeof StartupConfigFile.Mail | typeof StartupConfigFile.Middleware | typeof StartupConfigFile.Notification | typeof StartupConfigFile.Queue | typeof StartupConfigFile.Storage | typeof StartupConfigFile.Workers;
|
|
13
|
+
export type StartupConfigFileTypes = typeof StartupConfigFile.Broadcast | typeof StartupConfigFile.Cache | typeof StartupConfigFile.Database | typeof StartupConfigFile.Mail | typeof StartupConfigFile.Trace | typeof StartupConfigFile.Middleware | typeof StartupConfigFile.Notification | typeof StartupConfigFile.Queue | typeof StartupConfigFile.Storage | typeof StartupConfigFile.Workers;
|
|
13
14
|
export declare const StartupConfigFileRegistry: Readonly<{
|
|
14
15
|
preload(files: readonly StartupConfigFileTypes[]): Promise<void>;
|
|
15
16
|
isPreloaded(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartupConfigFileRegistry.d.ts","sourceRoot":"","sources":["../../../src/runtime/StartupConfigFileRegistry.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"StartupConfigFileRegistry.d.ts","sourceRoot":"","sources":["../../../src/runtime/StartupConfigFileRegistry.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,iBAAiB;;;;;;;;;;;CAWpB,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAC9B,OAAO,iBAAiB,CAAC,SAAS,GAClC,OAAO,iBAAiB,CAAC,KAAK,GAC9B,OAAO,iBAAiB,CAAC,QAAQ,GACjC,OAAO,iBAAiB,CAAC,IAAI,GAC7B,OAAO,iBAAiB,CAAC,KAAK,GAC9B,OAAO,iBAAiB,CAAC,UAAU,GACnC,OAAO,iBAAiB,CAAC,YAAY,GACrC,OAAO,iBAAiB,CAAC,KAAK,GAC9B,OAAO,iBAAiB,CAAC,OAAO,GAChC,OAAO,iBAAiB,CAAC,OAAO,CAAC;AAiDrC,eAAO,MAAM,yBAAyB;mBACf,SAAS,sBAAsB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;mBAcvD,OAAO;QAIlB,CAAC,QAAQ,sBAAsB,GAAG,CAAC,GAAG,SAAS;cAIzC,sBAAsB,GAAG,OAAO;IAI1C,+BAA+B;aACtB,IAAI;EAIb,CAAC;AAEH,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type TraceConfigApi = {
|
|
2
|
+
merge(overrides?: unknown): {
|
|
3
|
+
enabled?: boolean;
|
|
4
|
+
connection?: string;
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
type TraceStorageApi = {
|
|
8
|
+
resolveStorage(db: unknown): unknown;
|
|
9
|
+
};
|
|
10
|
+
export declare const isAvailable: () => boolean;
|
|
11
|
+
export declare const TraceConfig: TraceConfigApi;
|
|
12
|
+
export declare const TraceStorage: TraceStorageApi;
|
|
13
|
+
export declare const registerTraceDashboard: (router: unknown, options?: {
|
|
14
|
+
basePath?: string;
|
|
15
|
+
middleware?: ReadonlyArray<string>;
|
|
16
|
+
}) => void;
|
|
17
|
+
export declare const registerTraceRoutes: (router: unknown, storage: unknown, options?: {
|
|
18
|
+
basePath?: string;
|
|
19
|
+
middleware?: ReadonlyArray<string>;
|
|
20
|
+
}) => void;
|
|
21
|
+
export declare const captureTraceException: (error: unknown) => void;
|
|
22
|
+
export declare const ensureSystemTraceRegistered: () => Promise<void>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=trace-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-runtime.d.ts","sourceRoot":"","sources":["../../../../src/runtime/plugins/trace-runtime.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG;IACpB,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC;CACtC,CAAC;AA6DF,eAAO,MAAM,WAAW,QAAO,OAA0C,CAAC;AAE1E,eAAO,MAAM,WAAW,EAAE,cAIxB,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,eAIzB,CAAC;AAEH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,OAAO,EACf,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,KAClE,IAEF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,OAAO,EACf,SAAS,OAAO,EAChB,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,KAClE,IAEF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,KAAG,IAStD,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAa,OAAO,CAAC,IAAI,CAIhE,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const fallbackTraceConfig = Object.freeze({
|
|
2
|
+
merge: () => ({ enabled: false }),
|
|
3
|
+
});
|
|
4
|
+
const fallbackTraceStorage = Object.freeze({
|
|
5
|
+
resolveStorage: (_db) => undefined,
|
|
6
|
+
});
|
|
7
|
+
const fallbackRegisterTraceDashboard = (_router, _options) => undefined;
|
|
8
|
+
const fallbackRegisterTraceRoutes = (_router, _storage, _options) => undefined;
|
|
9
|
+
const fallbackCaptureTraceException = (_error) => undefined;
|
|
10
|
+
let systemTraceModule;
|
|
11
|
+
let didAttemptSystemTraceLoad = false;
|
|
12
|
+
let pendingSystemTraceLoad;
|
|
13
|
+
const loadSystemTraceModule = async () => {
|
|
14
|
+
if (systemTraceModule !== undefined)
|
|
15
|
+
return systemTraceModule;
|
|
16
|
+
if (didAttemptSystemTraceLoad && pendingSystemTraceLoad === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (pendingSystemTraceLoad !== undefined)
|
|
19
|
+
return pendingSystemTraceLoad;
|
|
20
|
+
pendingSystemTraceLoad = import('@zintrust/trace')
|
|
21
|
+
.then((module) => {
|
|
22
|
+
systemTraceModule = module;
|
|
23
|
+
return systemTraceModule;
|
|
24
|
+
})
|
|
25
|
+
.catch(() => undefined)
|
|
26
|
+
.finally(() => {
|
|
27
|
+
didAttemptSystemTraceLoad = true;
|
|
28
|
+
pendingSystemTraceLoad = undefined;
|
|
29
|
+
});
|
|
30
|
+
return pendingSystemTraceLoad;
|
|
31
|
+
};
|
|
32
|
+
export const isAvailable = () => systemTraceModule !== undefined;
|
|
33
|
+
export const TraceConfig = Object.freeze({
|
|
34
|
+
merge(overrides) {
|
|
35
|
+
return (systemTraceModule?.TraceConfig ?? fallbackTraceConfig).merge(overrides);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
export const TraceStorage = Object.freeze({
|
|
39
|
+
resolveStorage(db) {
|
|
40
|
+
return (systemTraceModule?.TraceStorage ?? fallbackTraceStorage).resolveStorage(db);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
export const registerTraceDashboard = (router, options) => {
|
|
44
|
+
(systemTraceModule?.registerTraceDashboard ?? fallbackRegisterTraceDashboard)(router, options);
|
|
45
|
+
};
|
|
46
|
+
export const registerTraceRoutes = (router, storage, options) => {
|
|
47
|
+
(systemTraceModule?.registerTraceRoutes ?? fallbackRegisterTraceRoutes)(router, storage, options);
|
|
48
|
+
};
|
|
49
|
+
export const captureTraceException = (error) => {
|
|
50
|
+
if (systemTraceModule?.captureTraceException !== undefined) {
|
|
51
|
+
systemTraceModule.captureTraceException(error);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
void loadSystemTraceModule().then((module) => {
|
|
55
|
+
(module?.captureTraceException ?? fallbackCaptureTraceException)(error);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
export const ensureSystemTraceRegistered = async () => {
|
|
59
|
+
const module = await loadSystemTraceModule();
|
|
60
|
+
if (module === undefined)
|
|
61
|
+
return;
|
|
62
|
+
await import('../../../packages/trace/src/register.js').catch(() => undefined);
|
|
63
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../../../src/runtime/plugins/trace.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,CAAC"}
|
|
@@ -7,8 +7,8 @@ const tryImport = async (specifier) => {
|
|
|
7
7
|
return false;
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
|
-
const importedPackagePlugin = await tryImport('@zintrust/
|
|
10
|
+
const importedPackagePlugin = await tryImport('@zintrust/trace/plugin');
|
|
11
11
|
if (!importedPackagePlugin) {
|
|
12
|
-
await import('../../../packages/
|
|
12
|
+
await import('../../../packages/trace/src/plugin.js').catch(() => undefined);
|
|
13
13
|
}
|
|
14
14
|
export {};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* JSON Web Token generation, verification, and claims management
|
|
4
4
|
* Uses native Node.js crypto module (zero external dependencies)
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { SystemTraceBridge } from '../trace/SystemTraceBridge.js';
|
|
7
7
|
import { securityConfig } from '../config/security.js';
|
|
8
8
|
import { ErrorFactory } from '../exceptions/ZintrustError.js';
|
|
9
9
|
import { createHmac, createSign, createVerify, randomBytes } from '../node-singletons/crypto.js';
|
|
@@ -44,11 +44,11 @@ const getLogoutSubject = (authHeader) => {
|
|
|
44
44
|
const logout = async (authHeader) => {
|
|
45
45
|
const subject = getLogoutSubject(authHeader);
|
|
46
46
|
await JwtSessions.logout(authHeader);
|
|
47
|
-
|
|
47
|
+
SystemTraceBridge.emitAuth('logout', subject);
|
|
48
48
|
};
|
|
49
49
|
const logoutAll = async (sub) => {
|
|
50
50
|
await JwtSessions.logoutAll(sub);
|
|
51
|
-
|
|
51
|
+
SystemTraceBridge.emitAuth('logout', sub);
|
|
52
52
|
};
|
|
53
53
|
const signAccessToken = async (payload, expiresIn) => {
|
|
54
54
|
const algorithm = securityConfig.jwt.algorithm;
|
|
@@ -75,7 +75,7 @@ const signAccessToken = async (payload, expiresIn) => {
|
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
77
|
await JwtSessions.register(token);
|
|
78
|
-
|
|
78
|
+
SystemTraceBridge.emitAuth('login', typeof payload.sub === 'string' ? payload.sub : undefined);
|
|
79
79
|
return token;
|
|
80
80
|
};
|
|
81
81
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"ignoreDeprecations": "6.0",
|
|
4
3
|
"baseUrl": ".",
|
|
5
4
|
"outDir": "./dist",
|
|
6
5
|
"rootDir": "./",
|
|
@@ -17,17 +16,17 @@
|
|
|
17
16
|
"@routes/*": ["./routes/*"],
|
|
18
17
|
"@database/*": ["./database/*"],
|
|
19
18
|
|
|
20
|
-
"@tools/*": ["app/Tools/*"],
|
|
21
|
-
"@httpClient/*": ["app/Tools/http/*"],
|
|
22
|
-
"@templates": ["app/Tools/templates/index.ts"],
|
|
23
|
-
"@templates/*": ["app/Tools/templates/*"],
|
|
24
|
-
"@mail/*": ["app/Tools/mail/*"],
|
|
25
|
-
"@storage": ["app/Tools/storage/index.ts"],
|
|
26
|
-
"@storage/*": ["app/Tools/storage/*"],
|
|
27
|
-
"@drivers/*": ["app/Tools/storage/drivers/*"],
|
|
28
|
-
"@notification/*": ["app/Tools/notification/*"],
|
|
29
|
-
"@broadcast/*": ["app/Tools/broadcast/*"],
|
|
30
|
-
"@queue/*": ["app/Tools/queue/*"]
|
|
19
|
+
"@tools/*": ["./app/Tools/*"],
|
|
20
|
+
"@httpClient/*": ["./app/Tools/http/*"],
|
|
21
|
+
"@templates": ["./app/Tools/templates/index.ts"],
|
|
22
|
+
"@templates/*": ["./app/Tools/templates/*"],
|
|
23
|
+
"@mail/*": ["./app/Tools/mail/*"],
|
|
24
|
+
"@storage": ["./app/Tools/storage/index.ts"],
|
|
25
|
+
"@storage/*": ["./app/Tools/storage/*"],
|
|
26
|
+
"@drivers/*": ["./app/Tools/storage/drivers/*"],
|
|
27
|
+
"@notification/*": ["./app/Tools/notification/*"],
|
|
28
|
+
"@broadcast/*": ["./app/Tools/broadcast/*"],
|
|
29
|
+
"@queue/*": ["./app/Tools/queue/*"]
|
|
31
30
|
}
|
|
32
31
|
},
|
|
33
32
|
"include": ["src/**/*", "app/**/*", "routes/**/*", "database/**/*"],
|