@axiom-lattice/gateway 2.1.29 → 2.1.30
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/.turbo/turbo-build.log +8 -8
- package/AGENTS.md +50 -0
- package/CHANGELOG.md +10 -0
- package/dist/index.js +1050 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1043 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/controllers/auth.ts +443 -0
- package/src/controllers/metrics-configs.ts +989 -0
- package/src/controllers/tenants.ts +121 -0
- package/src/controllers/users.ts +135 -0
- package/src/routes/index.ts +15 -0
package/dist/index.js
CHANGED
|
@@ -3097,6 +3097,1038 @@ function registerDatabaseConfigRoutes(app2) {
|
|
|
3097
3097
|
);
|
|
3098
3098
|
}
|
|
3099
3099
|
|
|
3100
|
+
// src/controllers/metrics-configs.ts
|
|
3101
|
+
var import_core18 = require("@axiom-lattice/core");
|
|
3102
|
+
var import_crypto4 = require("crypto");
|
|
3103
|
+
function getTenantId2(request) {
|
|
3104
|
+
return request.headers["x-tenant-id"] || "default";
|
|
3105
|
+
}
|
|
3106
|
+
async function getMetricsServerConfigList(request, reply) {
|
|
3107
|
+
const tenantId = getTenantId2(request);
|
|
3108
|
+
try {
|
|
3109
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3110
|
+
const store = storeLattice.store;
|
|
3111
|
+
const configs = await store.getAllConfigs(tenantId);
|
|
3112
|
+
return {
|
|
3113
|
+
success: true,
|
|
3114
|
+
message: "Metrics server configurations retrieved successfully",
|
|
3115
|
+
data: {
|
|
3116
|
+
records: configs,
|
|
3117
|
+
total: configs.length
|
|
3118
|
+
}
|
|
3119
|
+
};
|
|
3120
|
+
} catch (error) {
|
|
3121
|
+
console.error("Failed to get metrics server configs:", error);
|
|
3122
|
+
return {
|
|
3123
|
+
success: false,
|
|
3124
|
+
message: "Failed to retrieve metrics server configurations",
|
|
3125
|
+
data: {
|
|
3126
|
+
records: [],
|
|
3127
|
+
total: 0
|
|
3128
|
+
}
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
async function getMetricsServerConfig(request, reply) {
|
|
3133
|
+
const tenantId = getTenantId2(request);
|
|
3134
|
+
const { key } = request.params;
|
|
3135
|
+
try {
|
|
3136
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3137
|
+
const store = storeLattice.store;
|
|
3138
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3139
|
+
if (!config2) {
|
|
3140
|
+
return {
|
|
3141
|
+
success: false,
|
|
3142
|
+
message: "Metrics server configuration not found"
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
return {
|
|
3146
|
+
success: true,
|
|
3147
|
+
message: "Metrics server configuration retrieved successfully",
|
|
3148
|
+
data: config2
|
|
3149
|
+
};
|
|
3150
|
+
} catch (error) {
|
|
3151
|
+
console.error("Failed to get metrics server config:", error);
|
|
3152
|
+
return {
|
|
3153
|
+
success: false,
|
|
3154
|
+
message: "Failed to retrieve metrics server configuration"
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
async function createMetricsServerConfig(request, reply) {
|
|
3159
|
+
const tenantId = getTenantId2(request);
|
|
3160
|
+
const body = request.body;
|
|
3161
|
+
try {
|
|
3162
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3163
|
+
const store = storeLattice.store;
|
|
3164
|
+
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3165
|
+
if (existing) {
|
|
3166
|
+
reply.code(409);
|
|
3167
|
+
return {
|
|
3168
|
+
success: false,
|
|
3169
|
+
message: "Metrics server configuration with this key already exists"
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
if (body.config.type === "semantic" && !body.selectedDataSources) {
|
|
3173
|
+
reply.code(400);
|
|
3174
|
+
return {
|
|
3175
|
+
success: false,
|
|
3176
|
+
message: "selectedDataSources is required for semantic metrics servers"
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
const id = body.id || (0, import_crypto4.randomUUID)();
|
|
3180
|
+
const configData = {
|
|
3181
|
+
key: body.key,
|
|
3182
|
+
name: body.name,
|
|
3183
|
+
description: body.description,
|
|
3184
|
+
config: body.config.type === "semantic" ? {
|
|
3185
|
+
...body.config,
|
|
3186
|
+
selectedDataSources: body.selectedDataSources || []
|
|
3187
|
+
} : body.config
|
|
3188
|
+
};
|
|
3189
|
+
const config2 = await store.createConfig(tenantId, id, configData);
|
|
3190
|
+
try {
|
|
3191
|
+
import_core18.metricsServerManager.registerServer(config2.key, config2.config);
|
|
3192
|
+
} catch (error) {
|
|
3193
|
+
console.warn("Failed to auto-register metrics server:", error);
|
|
3194
|
+
}
|
|
3195
|
+
reply.code(201);
|
|
3196
|
+
return {
|
|
3197
|
+
success: true,
|
|
3198
|
+
message: "Metrics server configuration created successfully",
|
|
3199
|
+
data: config2
|
|
3200
|
+
};
|
|
3201
|
+
} catch (error) {
|
|
3202
|
+
console.error("Failed to create metrics server config:", error);
|
|
3203
|
+
return {
|
|
3204
|
+
success: false,
|
|
3205
|
+
message: "Failed to create metrics server configuration"
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
async function updateMetricsServerConfig(request, reply) {
|
|
3210
|
+
const tenantId = getTenantId2(request);
|
|
3211
|
+
const { key } = request.params;
|
|
3212
|
+
const updates = request.body;
|
|
3213
|
+
try {
|
|
3214
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3215
|
+
const store = storeLattice.store;
|
|
3216
|
+
const existing = await store.getConfigByKey(tenantId, key);
|
|
3217
|
+
if (!existing) {
|
|
3218
|
+
reply.code(404);
|
|
3219
|
+
return {
|
|
3220
|
+
success: false,
|
|
3221
|
+
message: "Metrics server configuration not found"
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
const isSemantic = updates.config?.type === "semantic" || existing.config.type === "semantic" && !updates.config?.type;
|
|
3225
|
+
if (isSemantic && updates.selectedDataSources !== void 0) {
|
|
3226
|
+
updates.config = {
|
|
3227
|
+
...existing.config,
|
|
3228
|
+
...updates.config,
|
|
3229
|
+
type: "semantic",
|
|
3230
|
+
selectedDataSources: updates.selectedDataSources
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
const updated = await store.updateConfig(tenantId, existing.id, updates);
|
|
3234
|
+
if (!updated) {
|
|
3235
|
+
return {
|
|
3236
|
+
success: false,
|
|
3237
|
+
message: "Failed to update metrics server configuration"
|
|
3238
|
+
};
|
|
3239
|
+
}
|
|
3240
|
+
if (updates.config) {
|
|
3241
|
+
try {
|
|
3242
|
+
import_core18.metricsServerManager.registerServer(updated.key, updated.config);
|
|
3243
|
+
} catch (error) {
|
|
3244
|
+
console.warn("Failed to re-register metrics server:", error);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
return {
|
|
3248
|
+
success: true,
|
|
3249
|
+
message: "Metrics server configuration updated successfully",
|
|
3250
|
+
data: updated
|
|
3251
|
+
};
|
|
3252
|
+
} catch (error) {
|
|
3253
|
+
console.error("Failed to update metrics server config:", error);
|
|
3254
|
+
return {
|
|
3255
|
+
success: false,
|
|
3256
|
+
message: "Failed to update metrics server configuration"
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
async function deleteMetricsServerConfig(request, reply) {
|
|
3261
|
+
const tenantId = getTenantId2(request);
|
|
3262
|
+
const { keyOrId } = request.params;
|
|
3263
|
+
try {
|
|
3264
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3265
|
+
const store = storeLattice.store;
|
|
3266
|
+
let config2 = await store.getConfigByKey(tenantId, keyOrId);
|
|
3267
|
+
let configKey = keyOrId;
|
|
3268
|
+
if (!config2) {
|
|
3269
|
+
config2 = await store.getConfigById(tenantId, keyOrId);
|
|
3270
|
+
if (config2) {
|
|
3271
|
+
configKey = config2.key;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
if (!config2) {
|
|
3275
|
+
reply.code(404);
|
|
3276
|
+
return {
|
|
3277
|
+
success: false,
|
|
3278
|
+
message: "Metrics server configuration not found"
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
const deleted = await store.deleteConfig(tenantId, config2.id);
|
|
3282
|
+
if (!deleted) {
|
|
3283
|
+
return {
|
|
3284
|
+
success: false,
|
|
3285
|
+
message: "Failed to delete metrics server configuration"
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
try {
|
|
3289
|
+
if (import_core18.metricsServerManager.hasServer(configKey)) {
|
|
3290
|
+
import_core18.metricsServerManager.removeServer(configKey);
|
|
3291
|
+
}
|
|
3292
|
+
} catch (error) {
|
|
3293
|
+
console.warn("Failed to remove from MetricsServerManager:", error);
|
|
3294
|
+
}
|
|
3295
|
+
return {
|
|
3296
|
+
success: true,
|
|
3297
|
+
message: "Metrics server configuration deleted successfully"
|
|
3298
|
+
};
|
|
3299
|
+
} catch (error) {
|
|
3300
|
+
console.error("Failed to delete metrics server config:", error);
|
|
3301
|
+
return {
|
|
3302
|
+
success: false,
|
|
3303
|
+
message: "Failed to delete metrics server configuration"
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
async function testMetricsServerConnection(request, reply) {
|
|
3308
|
+
const tenantId = getTenantId2(request);
|
|
3309
|
+
const { key } = request.params;
|
|
3310
|
+
try {
|
|
3311
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3312
|
+
const store = storeLattice.store;
|
|
3313
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3314
|
+
if (!config2) {
|
|
3315
|
+
reply.code(404);
|
|
3316
|
+
return {
|
|
3317
|
+
success: false,
|
|
3318
|
+
message: "Metrics server configuration not found"
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
const testKey = `__test_${key}_${Date.now()}`;
|
|
3322
|
+
import_core18.metricsServerManager.registerServer(testKey, config2.config);
|
|
3323
|
+
try {
|
|
3324
|
+
const client = import_core18.metricsServerManager.getClient(testKey);
|
|
3325
|
+
const result = await client.testConnection();
|
|
3326
|
+
import_core18.metricsServerManager.removeServer(testKey);
|
|
3327
|
+
return {
|
|
3328
|
+
success: true,
|
|
3329
|
+
message: result.connected ? "Connection test successful" : "Connection test failed",
|
|
3330
|
+
data: result
|
|
3331
|
+
};
|
|
3332
|
+
} catch (error) {
|
|
3333
|
+
try {
|
|
3334
|
+
import_core18.metricsServerManager.removeServer(testKey);
|
|
3335
|
+
} catch {
|
|
3336
|
+
}
|
|
3337
|
+
return {
|
|
3338
|
+
success: true,
|
|
3339
|
+
message: "Connection test failed",
|
|
3340
|
+
data: {
|
|
3341
|
+
connected: false,
|
|
3342
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
} catch (error) {
|
|
3347
|
+
console.error("Failed to test metrics server connection:", error);
|
|
3348
|
+
return {
|
|
3349
|
+
success: false,
|
|
3350
|
+
message: "Failed to test metrics server connection",
|
|
3351
|
+
data: {
|
|
3352
|
+
connected: false,
|
|
3353
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
async function listAvailableMetrics(request, reply) {
|
|
3359
|
+
const tenantId = getTenantId2(request);
|
|
3360
|
+
const { key } = request.params;
|
|
3361
|
+
try {
|
|
3362
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3363
|
+
const store = storeLattice.store;
|
|
3364
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3365
|
+
if (!config2) {
|
|
3366
|
+
reply.code(404);
|
|
3367
|
+
return {
|
|
3368
|
+
success: false,
|
|
3369
|
+
message: "Metrics server configuration not found"
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
if (!import_core18.metricsServerManager.hasServer(key)) {
|
|
3373
|
+
import_core18.metricsServerManager.registerServer(key, config2.config);
|
|
3374
|
+
}
|
|
3375
|
+
const client = import_core18.metricsServerManager.getClient(key);
|
|
3376
|
+
const metrics = await client.listMetrics();
|
|
3377
|
+
return {
|
|
3378
|
+
success: true,
|
|
3379
|
+
message: "Metrics retrieved successfully",
|
|
3380
|
+
data: {
|
|
3381
|
+
metrics: metrics.map((m) => ({
|
|
3382
|
+
name: m.name,
|
|
3383
|
+
type: m.type,
|
|
3384
|
+
description: m.description
|
|
3385
|
+
}))
|
|
3386
|
+
}
|
|
3387
|
+
};
|
|
3388
|
+
} catch (error) {
|
|
3389
|
+
console.error("Failed to list metrics:", error);
|
|
3390
|
+
return {
|
|
3391
|
+
success: false,
|
|
3392
|
+
message: "Failed to retrieve metrics"
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
async function queryMetricsData(request, reply) {
|
|
3397
|
+
const tenantId = getTenantId2(request);
|
|
3398
|
+
const { key } = request.params;
|
|
3399
|
+
const { metricName, startTime, endTime, step, labels } = request.body;
|
|
3400
|
+
try {
|
|
3401
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3402
|
+
const store = storeLattice.store;
|
|
3403
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3404
|
+
if (!config2) {
|
|
3405
|
+
reply.code(404);
|
|
3406
|
+
return {
|
|
3407
|
+
success: false,
|
|
3408
|
+
message: "Metrics server configuration not found"
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
if (!metricName) {
|
|
3412
|
+
reply.code(400);
|
|
3413
|
+
return {
|
|
3414
|
+
success: false,
|
|
3415
|
+
message: "metricName is required"
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
if (!import_core18.metricsServerManager.hasServer(key)) {
|
|
3419
|
+
import_core18.metricsServerManager.registerServer(key, config2.config);
|
|
3420
|
+
}
|
|
3421
|
+
const client = import_core18.metricsServerManager.getClient(key);
|
|
3422
|
+
const result = await client.queryMetricData(metricName, {
|
|
3423
|
+
startTime,
|
|
3424
|
+
endTime,
|
|
3425
|
+
step,
|
|
3426
|
+
labels
|
|
3427
|
+
});
|
|
3428
|
+
return {
|
|
3429
|
+
success: true,
|
|
3430
|
+
message: "Metric data retrieved successfully",
|
|
3431
|
+
data: {
|
|
3432
|
+
metricName: result.metricName,
|
|
3433
|
+
dataPoints: result.dataPoints
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3436
|
+
} catch (error) {
|
|
3437
|
+
console.error("Failed to query metric data:", error);
|
|
3438
|
+
return {
|
|
3439
|
+
success: false,
|
|
3440
|
+
message: "Failed to query metric data"
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
async function getDataSources(request, reply) {
|
|
3445
|
+
const tenantId = getTenantId2(request);
|
|
3446
|
+
const { key } = request.params;
|
|
3447
|
+
try {
|
|
3448
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3449
|
+
const store = storeLattice.store;
|
|
3450
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3451
|
+
if (!config2) {
|
|
3452
|
+
reply.code(404);
|
|
3453
|
+
return {
|
|
3454
|
+
success: false,
|
|
3455
|
+
message: "Metrics server configuration not found"
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
if (config2.config.type !== "semantic") {
|
|
3459
|
+
reply.code(400);
|
|
3460
|
+
return {
|
|
3461
|
+
success: false,
|
|
3462
|
+
message: "This endpoint is only available for semantic metrics servers"
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
const semanticConfig = config2.config;
|
|
3466
|
+
const client = new import_core18.SemanticMetricsClient(semanticConfig);
|
|
3467
|
+
const datasources = await client.getDataSources();
|
|
3468
|
+
return {
|
|
3469
|
+
success: true,
|
|
3470
|
+
message: "Data sources retrieved successfully",
|
|
3471
|
+
data: {
|
|
3472
|
+
datasources
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
} catch (error) {
|
|
3476
|
+
console.error("Failed to get data sources:", error);
|
|
3477
|
+
return {
|
|
3478
|
+
success: false,
|
|
3479
|
+
message: "Failed to retrieve data sources"
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
async function getDatasourceMetrics(request, reply) {
|
|
3484
|
+
const tenantId = getTenantId2(request);
|
|
3485
|
+
const { key, datasourceId } = request.params;
|
|
3486
|
+
try {
|
|
3487
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3488
|
+
const store = storeLattice.store;
|
|
3489
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3490
|
+
if (!config2) {
|
|
3491
|
+
reply.code(404);
|
|
3492
|
+
return {
|
|
3493
|
+
success: false,
|
|
3494
|
+
message: "Metrics server configuration not found"
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3497
|
+
if (config2.config.type !== "semantic") {
|
|
3498
|
+
reply.code(400);
|
|
3499
|
+
return {
|
|
3500
|
+
success: false,
|
|
3501
|
+
message: "This endpoint is only available for semantic metrics servers"
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3504
|
+
const semanticConfig = config2.config;
|
|
3505
|
+
const client = new import_core18.SemanticMetricsClient(semanticConfig);
|
|
3506
|
+
const metrics = await client.getDatasourceMetrics(datasourceId);
|
|
3507
|
+
return {
|
|
3508
|
+
success: true,
|
|
3509
|
+
message: "Datasource metrics retrieved successfully",
|
|
3510
|
+
data: metrics
|
|
3511
|
+
};
|
|
3512
|
+
} catch (error) {
|
|
3513
|
+
console.error("Failed to get datasource metrics:", error);
|
|
3514
|
+
return {
|
|
3515
|
+
success: false,
|
|
3516
|
+
message: "Failed to retrieve datasource metrics"
|
|
3517
|
+
};
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
async function querySemanticMetrics(request, reply) {
|
|
3521
|
+
const tenantId = getTenantId2(request);
|
|
3522
|
+
const { key } = request.params;
|
|
3523
|
+
const body = request.body;
|
|
3524
|
+
try {
|
|
3525
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3526
|
+
const store = storeLattice.store;
|
|
3527
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3528
|
+
if (!config2) {
|
|
3529
|
+
reply.code(404);
|
|
3530
|
+
return {
|
|
3531
|
+
success: false,
|
|
3532
|
+
message: "Metrics server configuration not found"
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
if (config2.config.type !== "semantic") {
|
|
3536
|
+
reply.code(400);
|
|
3537
|
+
return {
|
|
3538
|
+
success: false,
|
|
3539
|
+
message: "This endpoint is only available for semantic metrics servers"
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
if (!body.datasourceId || !body.metrics || body.metrics.length === 0) {
|
|
3543
|
+
reply.code(400);
|
|
3544
|
+
return {
|
|
3545
|
+
success: false,
|
|
3546
|
+
message: "datasourceId and metrics array are required"
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
const semanticConfig = config2.config;
|
|
3550
|
+
const client = new import_core18.SemanticMetricsClient(semanticConfig);
|
|
3551
|
+
const result = await client.semanticQuery(body);
|
|
3552
|
+
return {
|
|
3553
|
+
success: true,
|
|
3554
|
+
message: "Semantic query executed successfully",
|
|
3555
|
+
data: {
|
|
3556
|
+
datasourceId: result.datasourceId,
|
|
3557
|
+
metrics: result.metrics,
|
|
3558
|
+
dataPoints: result.dataPoints,
|
|
3559
|
+
metadata: result.metadata
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
} catch (error) {
|
|
3563
|
+
console.error("Failed to query semantic metrics:", error);
|
|
3564
|
+
return {
|
|
3565
|
+
success: false,
|
|
3566
|
+
message: "Failed to query semantic metrics"
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
async function testSemanticDataSources(request, reply) {
|
|
3571
|
+
const body = request.body;
|
|
3572
|
+
try {
|
|
3573
|
+
if (!body.serverUrl) {
|
|
3574
|
+
reply.code(400);
|
|
3575
|
+
return {
|
|
3576
|
+
success: false,
|
|
3577
|
+
message: "serverUrl is required"
|
|
3578
|
+
};
|
|
3579
|
+
}
|
|
3580
|
+
const testConfig = {
|
|
3581
|
+
type: "semantic",
|
|
3582
|
+
serverUrl: body.serverUrl,
|
|
3583
|
+
apiKey: body.apiKey,
|
|
3584
|
+
username: body.username,
|
|
3585
|
+
password: body.password,
|
|
3586
|
+
headers: body.headers
|
|
3587
|
+
};
|
|
3588
|
+
const client = new import_core18.SemanticMetricsClient(testConfig);
|
|
3589
|
+
const datasources = await client.getDataSources();
|
|
3590
|
+
return {
|
|
3591
|
+
success: true,
|
|
3592
|
+
message: "Data sources retrieved successfully",
|
|
3593
|
+
data: {
|
|
3594
|
+
datasources
|
|
3595
|
+
}
|
|
3596
|
+
};
|
|
3597
|
+
} catch (error) {
|
|
3598
|
+
console.error("Failed to test datasources:", error);
|
|
3599
|
+
return {
|
|
3600
|
+
success: false,
|
|
3601
|
+
message: `Failed to retrieve data sources: ${error instanceof Error ? error.message : String(error)}`
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
async function testDatasourceMetrics(request, reply) {
|
|
3606
|
+
const { datasourceId } = request.params;
|
|
3607
|
+
const body = request.body;
|
|
3608
|
+
try {
|
|
3609
|
+
if (!body.serverUrl) {
|
|
3610
|
+
reply.code(400);
|
|
3611
|
+
return {
|
|
3612
|
+
success: false,
|
|
3613
|
+
message: "serverUrl is required"
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
const testConfig = {
|
|
3617
|
+
type: "semantic",
|
|
3618
|
+
serverUrl: body.serverUrl,
|
|
3619
|
+
apiKey: body.apiKey,
|
|
3620
|
+
username: body.username,
|
|
3621
|
+
password: body.password,
|
|
3622
|
+
headers: body.headers
|
|
3623
|
+
};
|
|
3624
|
+
const client = new import_core18.SemanticMetricsClient(testConfig);
|
|
3625
|
+
const metrics = await client.getDatasourceMetrics(datasourceId);
|
|
3626
|
+
return {
|
|
3627
|
+
success: true,
|
|
3628
|
+
message: "Datasource metrics retrieved successfully",
|
|
3629
|
+
data: metrics
|
|
3630
|
+
};
|
|
3631
|
+
} catch (error) {
|
|
3632
|
+
console.error("Failed to test datasource metrics:", error);
|
|
3633
|
+
return {
|
|
3634
|
+
success: false,
|
|
3635
|
+
message: `Failed to retrieve datasource metrics: ${error instanceof Error ? error.message : String(error)}`
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
function registerMetricsServerConfigRoutes(app2) {
|
|
3640
|
+
app2.get("/api/metrics-configs", getMetricsServerConfigList);
|
|
3641
|
+
app2.get("/api/metrics-configs/:key", getMetricsServerConfig);
|
|
3642
|
+
app2.post("/api/metrics-configs", createMetricsServerConfig);
|
|
3643
|
+
app2.put("/api/metrics-configs/:key", updateMetricsServerConfig);
|
|
3644
|
+
app2.delete("/api/metrics-configs/:keyOrId", deleteMetricsServerConfig);
|
|
3645
|
+
app2.post("/api/metrics-configs/:key/test", testMetricsServerConnection);
|
|
3646
|
+
app2.get("/api/metrics-configs/:key/metrics", listAvailableMetrics);
|
|
3647
|
+
app2.post("/api/metrics-configs/:key/query", queryMetricsData);
|
|
3648
|
+
app2.get("/api/metrics-configs/:key/datasources", getDataSources);
|
|
3649
|
+
app2.get("/api/metrics-configs/:key/datasources/:datasourceId/meta", getDatasourceMetrics);
|
|
3650
|
+
app2.post("/api/metrics-configs/:key/semantic-query", querySemanticMetrics);
|
|
3651
|
+
app2.post("/api/metrics-configs/test-datasources", testSemanticDataSources);
|
|
3652
|
+
app2.post("/api/metrics-configs/test-datasources/:datasourceId/meta", testDatasourceMetrics);
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
// src/controllers/users.ts
|
|
3656
|
+
var import_core19 = require("@axiom-lattice/core");
|
|
3657
|
+
var import_uuid4 = require("uuid");
|
|
3658
|
+
var UsersController = class {
|
|
3659
|
+
constructor() {
|
|
3660
|
+
this.userStore = (0, import_core19.getStoreLattice)("default", "user").store;
|
|
3661
|
+
}
|
|
3662
|
+
async listUsers(request, reply) {
|
|
3663
|
+
const { email } = request.query;
|
|
3664
|
+
if (email) {
|
|
3665
|
+
const user = await this.userStore.getUserByEmail(email);
|
|
3666
|
+
return { success: true, data: user ? [user] : [] };
|
|
3667
|
+
}
|
|
3668
|
+
const users = await this.userStore.getAllUsers();
|
|
3669
|
+
return { success: true, data: users };
|
|
3670
|
+
}
|
|
3671
|
+
async createUser(request, reply) {
|
|
3672
|
+
const data = request.body;
|
|
3673
|
+
const id = (0, import_uuid4.v4)();
|
|
3674
|
+
const existingUser = await this.userStore.getUserByEmail(data.email);
|
|
3675
|
+
if (existingUser) {
|
|
3676
|
+
return reply.status(409).send({
|
|
3677
|
+
success: false,
|
|
3678
|
+
error: `User with email ${data.email} already exists`
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
const user = await this.userStore.createUser(id, data);
|
|
3682
|
+
return reply.status(201).send({ success: true, data: user });
|
|
3683
|
+
}
|
|
3684
|
+
async getUser(request, reply) {
|
|
3685
|
+
const { userId } = request.params;
|
|
3686
|
+
const user = await this.userStore.getUserById(userId);
|
|
3687
|
+
if (!user) {
|
|
3688
|
+
return reply.status(404).send({
|
|
3689
|
+
success: false,
|
|
3690
|
+
error: "User not found"
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
return { success: true, data: user };
|
|
3694
|
+
}
|
|
3695
|
+
async updateUser(request, reply) {
|
|
3696
|
+
const { userId } = request.params;
|
|
3697
|
+
const updates = request.body;
|
|
3698
|
+
if (updates.email) {
|
|
3699
|
+
const existingUser = await this.userStore.getUserByEmail(updates.email);
|
|
3700
|
+
if (existingUser && existingUser.id !== userId) {
|
|
3701
|
+
return reply.status(409).send({
|
|
3702
|
+
success: false,
|
|
3703
|
+
error: `User with email ${updates.email} already exists`
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
const user = await this.userStore.updateUser(userId, updates);
|
|
3708
|
+
if (!user) {
|
|
3709
|
+
return reply.status(404).send({
|
|
3710
|
+
success: false,
|
|
3711
|
+
error: "User not found"
|
|
3712
|
+
});
|
|
3713
|
+
}
|
|
3714
|
+
return { success: true, data: user };
|
|
3715
|
+
}
|
|
3716
|
+
async deleteUser(request, reply) {
|
|
3717
|
+
const { userId } = request.params;
|
|
3718
|
+
const deleted = await this.userStore.deleteUser(userId);
|
|
3719
|
+
if (!deleted) {
|
|
3720
|
+
return reply.status(404).send({
|
|
3721
|
+
success: false,
|
|
3722
|
+
error: "User not found"
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
return { success: true };
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
function registerUserRoutes(app2) {
|
|
3729
|
+
const controller = new UsersController();
|
|
3730
|
+
app2.get("/api/users", controller.listUsers.bind(controller));
|
|
3731
|
+
app2.post("/api/users", controller.createUser.bind(controller));
|
|
3732
|
+
app2.get("/api/users/:userId", controller.getUser.bind(controller));
|
|
3733
|
+
app2.patch("/api/users/:userId", controller.updateUser.bind(controller));
|
|
3734
|
+
app2.delete("/api/users/:userId", controller.deleteUser.bind(controller));
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
// src/controllers/tenants.ts
|
|
3738
|
+
var import_core20 = require("@axiom-lattice/core");
|
|
3739
|
+
var import_uuid5 = require("uuid");
|
|
3740
|
+
var TenantsController = class {
|
|
3741
|
+
constructor() {
|
|
3742
|
+
this.tenantStore = (0, import_core20.getStoreLattice)("default", "tenant").store;
|
|
3743
|
+
}
|
|
3744
|
+
// ==================== Tenant CRUD ====================
|
|
3745
|
+
async listTenants(request, reply) {
|
|
3746
|
+
const tenants = await this.tenantStore.getAllTenants();
|
|
3747
|
+
return { success: true, data: tenants };
|
|
3748
|
+
}
|
|
3749
|
+
async createTenant(request, reply) {
|
|
3750
|
+
const data = request.body;
|
|
3751
|
+
const id = (0, import_uuid5.v4)();
|
|
3752
|
+
const tenant = await this.tenantStore.createTenant(id, data);
|
|
3753
|
+
return reply.status(201).send({ success: true, data: tenant });
|
|
3754
|
+
}
|
|
3755
|
+
async getTenant(request, reply) {
|
|
3756
|
+
const { tenantId } = request.params;
|
|
3757
|
+
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
3758
|
+
if (!tenant) {
|
|
3759
|
+
return reply.status(404).send({
|
|
3760
|
+
success: false,
|
|
3761
|
+
error: "Tenant not found"
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3764
|
+
return { success: true, data: tenant };
|
|
3765
|
+
}
|
|
3766
|
+
async updateTenant(request, reply) {
|
|
3767
|
+
const { tenantId } = request.params;
|
|
3768
|
+
const updates = request.body;
|
|
3769
|
+
const tenant = await this.tenantStore.updateTenant(tenantId, updates);
|
|
3770
|
+
if (!tenant) {
|
|
3771
|
+
return reply.status(404).send({
|
|
3772
|
+
success: false,
|
|
3773
|
+
error: "Tenant not found"
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
return { success: true, data: tenant };
|
|
3777
|
+
}
|
|
3778
|
+
async deleteTenant(request, reply) {
|
|
3779
|
+
const { tenantId } = request.params;
|
|
3780
|
+
if (tenantId === "default") {
|
|
3781
|
+
return reply.status(403).send({
|
|
3782
|
+
success: false,
|
|
3783
|
+
error: "Cannot delete the default tenant"
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
const deleted = await this.tenantStore.deleteTenant(tenantId);
|
|
3787
|
+
if (!deleted) {
|
|
3788
|
+
return reply.status(404).send({
|
|
3789
|
+
success: false,
|
|
3790
|
+
error: "Tenant not found"
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
return { success: true };
|
|
3794
|
+
}
|
|
3795
|
+
};
|
|
3796
|
+
function registerTenantRoutes(app2) {
|
|
3797
|
+
const controller = new TenantsController();
|
|
3798
|
+
app2.get("/api/tenants", controller.listTenants.bind(controller));
|
|
3799
|
+
app2.post("/api/tenants", controller.createTenant.bind(controller));
|
|
3800
|
+
app2.get("/api/tenants/:tenantId", controller.getTenant.bind(controller));
|
|
3801
|
+
app2.patch("/api/tenants/:tenantId", controller.updateTenant.bind(controller));
|
|
3802
|
+
app2.delete("/api/tenants/:tenantId", controller.deleteTenant.bind(controller));
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
// src/controllers/auth.ts
|
|
3806
|
+
var import_core21 = require("@axiom-lattice/core");
|
|
3807
|
+
var import_uuid6 = require("uuid");
|
|
3808
|
+
var defaultAuthConfig = {
|
|
3809
|
+
autoApproveUsers: true,
|
|
3810
|
+
allowTenantRegistration: true,
|
|
3811
|
+
tokenExpiration: 86400
|
|
3812
|
+
};
|
|
3813
|
+
var AuthController = class {
|
|
3814
|
+
constructor(config2 = {}) {
|
|
3815
|
+
this.userStore = (0, import_core21.getStoreLattice)("default", "user").store;
|
|
3816
|
+
this.tenantStore = (0, import_core21.getStoreLattice)("default", "tenant").store;
|
|
3817
|
+
this.userTenantLinkStore = (0, import_core21.getStoreLattice)("default", "userTenantLink").store;
|
|
3818
|
+
this.config = { ...defaultAuthConfig, ...config2 };
|
|
3819
|
+
}
|
|
3820
|
+
async register(request, reply) {
|
|
3821
|
+
const { email, password, name } = request.body;
|
|
3822
|
+
try {
|
|
3823
|
+
const existingUser = await this.userStore.getUserByEmail(email);
|
|
3824
|
+
if (existingUser) {
|
|
3825
|
+
return reply.status(409).send({
|
|
3826
|
+
success: false,
|
|
3827
|
+
error: "User with this email already exists"
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
const userId = (0, import_uuid6.v4)();
|
|
3831
|
+
const userStatus = this.config.autoApproveUsers ? "active" : "pending";
|
|
3832
|
+
const userData = {
|
|
3833
|
+
email,
|
|
3834
|
+
name,
|
|
3835
|
+
status: userStatus,
|
|
3836
|
+
metadata: {
|
|
3837
|
+
passwordHash: await this.hashPassword(password)
|
|
3838
|
+
}
|
|
3839
|
+
};
|
|
3840
|
+
const user = await this.userStore.createUser(userId, userData);
|
|
3841
|
+
return reply.status(201).send({
|
|
3842
|
+
success: true,
|
|
3843
|
+
message: this.config.autoApproveUsers ? "Registration successful" : "Registration successful. Please wait for admin approval.",
|
|
3844
|
+
data: {
|
|
3845
|
+
user: {
|
|
3846
|
+
id: user.id,
|
|
3847
|
+
email: user.email,
|
|
3848
|
+
name: user.name,
|
|
3849
|
+
status: user.status
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
});
|
|
3853
|
+
} catch (error) {
|
|
3854
|
+
console.error("Registration error:", error);
|
|
3855
|
+
return reply.status(500).send({
|
|
3856
|
+
success: false,
|
|
3857
|
+
error: "Registration failed"
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
async login(request, reply) {
|
|
3862
|
+
const { email, password } = request.body;
|
|
3863
|
+
try {
|
|
3864
|
+
const user = await this.userStore.getUserByEmail(email);
|
|
3865
|
+
if (!user) {
|
|
3866
|
+
return reply.status(401).send({
|
|
3867
|
+
success: false,
|
|
3868
|
+
error: "Invalid credentials"
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
if (user.status !== "active") {
|
|
3872
|
+
return reply.status(403).send({
|
|
3873
|
+
success: false,
|
|
3874
|
+
error: `Account is ${user.status}. Please wait for admin approval.`
|
|
3875
|
+
});
|
|
3876
|
+
}
|
|
3877
|
+
const isValidPassword = await this.verifyPassword(
|
|
3878
|
+
password,
|
|
3879
|
+
user.metadata?.passwordHash || ""
|
|
3880
|
+
);
|
|
3881
|
+
if (!isValidPassword) {
|
|
3882
|
+
return reply.status(401).send({
|
|
3883
|
+
success: false,
|
|
3884
|
+
error: "Invalid credentials"
|
|
3885
|
+
});
|
|
3886
|
+
}
|
|
3887
|
+
const tenantLinks = await this.userTenantLinkStore.getTenantsByUser(user.id);
|
|
3888
|
+
const token = await this.generateToken(user.id);
|
|
3889
|
+
return {
|
|
3890
|
+
success: true,
|
|
3891
|
+
data: {
|
|
3892
|
+
user: {
|
|
3893
|
+
id: user.id,
|
|
3894
|
+
email: user.email,
|
|
3895
|
+
name: user.name,
|
|
3896
|
+
status: user.status
|
|
3897
|
+
},
|
|
3898
|
+
tenants: tenantLinks.map((link) => ({
|
|
3899
|
+
id: link.tenantId,
|
|
3900
|
+
role: link.role
|
|
3901
|
+
})),
|
|
3902
|
+
token,
|
|
3903
|
+
requiresTenantSelection: tenantLinks.length > 1,
|
|
3904
|
+
hasTenants: tenantLinks.length > 0
|
|
3905
|
+
}
|
|
3906
|
+
};
|
|
3907
|
+
} catch (error) {
|
|
3908
|
+
console.error("Login error:", error);
|
|
3909
|
+
return reply.status(500).send({
|
|
3910
|
+
success: false,
|
|
3911
|
+
error: "Login failed"
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
async getUserTenants(request, reply) {
|
|
3916
|
+
const userId = request.user?.id;
|
|
3917
|
+
if (!userId) {
|
|
3918
|
+
return reply.status(401).send({
|
|
3919
|
+
success: false,
|
|
3920
|
+
error: "Unauthorized"
|
|
3921
|
+
});
|
|
3922
|
+
}
|
|
3923
|
+
try {
|
|
3924
|
+
const links = await this.userTenantLinkStore.getTenantsByUser(userId);
|
|
3925
|
+
const tenantsWithDetails = await Promise.all(
|
|
3926
|
+
links.map(async (link) => {
|
|
3927
|
+
const tenant = await this.tenantStore.getTenantById(link.tenantId);
|
|
3928
|
+
return {
|
|
3929
|
+
...link,
|
|
3930
|
+
tenant: tenant || null
|
|
3931
|
+
};
|
|
3932
|
+
})
|
|
3933
|
+
);
|
|
3934
|
+
return {
|
|
3935
|
+
success: true,
|
|
3936
|
+
data: tenantsWithDetails
|
|
3937
|
+
};
|
|
3938
|
+
} catch (error) {
|
|
3939
|
+
console.error("Get user tenants error:", error);
|
|
3940
|
+
return reply.status(500).send({
|
|
3941
|
+
success: false,
|
|
3942
|
+
error: "Failed to get user tenants"
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
async selectTenant(request, reply) {
|
|
3947
|
+
const userId = request.user?.id;
|
|
3948
|
+
const { tenantId } = request.body;
|
|
3949
|
+
if (!userId) {
|
|
3950
|
+
return reply.status(401).send({
|
|
3951
|
+
success: false,
|
|
3952
|
+
error: "Unauthorized"
|
|
3953
|
+
});
|
|
3954
|
+
}
|
|
3955
|
+
try {
|
|
3956
|
+
const hasLink = await this.userTenantLinkStore.hasLink(userId, tenantId);
|
|
3957
|
+
if (!hasLink) {
|
|
3958
|
+
return reply.status(403).send({
|
|
3959
|
+
success: false,
|
|
3960
|
+
error: "You do not have access to this tenant"
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
3964
|
+
if (!tenant) {
|
|
3965
|
+
return reply.status(404).send({
|
|
3966
|
+
success: false,
|
|
3967
|
+
error: "Tenant not found"
|
|
3968
|
+
});
|
|
3969
|
+
}
|
|
3970
|
+
const token = await this.generateToken(userId, tenantId);
|
|
3971
|
+
return {
|
|
3972
|
+
success: true,
|
|
3973
|
+
data: {
|
|
3974
|
+
tenant: {
|
|
3975
|
+
id: tenant.id,
|
|
3976
|
+
name: tenant.name,
|
|
3977
|
+
status: tenant.status
|
|
3978
|
+
},
|
|
3979
|
+
token
|
|
3980
|
+
}
|
|
3981
|
+
};
|
|
3982
|
+
} catch (error) {
|
|
3983
|
+
console.error("Select tenant error:", error);
|
|
3984
|
+
return reply.status(500).send({
|
|
3985
|
+
success: false,
|
|
3986
|
+
error: "Failed to select tenant"
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
async approveUser(request, reply) {
|
|
3991
|
+
const { userId, approved } = request.body;
|
|
3992
|
+
try {
|
|
3993
|
+
const user = await this.userStore.getUserById(userId);
|
|
3994
|
+
if (!user) {
|
|
3995
|
+
return reply.status(404).send({
|
|
3996
|
+
success: false,
|
|
3997
|
+
error: "User not found"
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
if (user.status !== "pending") {
|
|
4001
|
+
return reply.status(400).send({
|
|
4002
|
+
success: false,
|
|
4003
|
+
error: `User is already ${user.status}`
|
|
4004
|
+
});
|
|
4005
|
+
}
|
|
4006
|
+
const updatedUser = await this.userStore.updateUser(userId, {
|
|
4007
|
+
status: approved ? "active" : "suspended"
|
|
4008
|
+
});
|
|
4009
|
+
return {
|
|
4010
|
+
success: true,
|
|
4011
|
+
data: updatedUser
|
|
4012
|
+
};
|
|
4013
|
+
} catch (error) {
|
|
4014
|
+
console.error("Approval error:", error);
|
|
4015
|
+
return reply.status(500).send({
|
|
4016
|
+
success: false,
|
|
4017
|
+
error: "Approval failed"
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
async listPendingUsers(request, reply) {
|
|
4022
|
+
try {
|
|
4023
|
+
const users = await this.userStore.getAllUsers();
|
|
4024
|
+
const pendingUsers = users.filter((u) => u.status === "pending");
|
|
4025
|
+
return {
|
|
4026
|
+
success: true,
|
|
4027
|
+
data: pendingUsers
|
|
4028
|
+
};
|
|
4029
|
+
} catch (error) {
|
|
4030
|
+
console.error("List pending users error:", error);
|
|
4031
|
+
return reply.status(500).send({
|
|
4032
|
+
success: false,
|
|
4033
|
+
error: "Failed to list pending users"
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
async assignTenant(request, reply) {
|
|
4038
|
+
const { userId, tenantId, role = "member" } = request.body;
|
|
4039
|
+
try {
|
|
4040
|
+
const user = await this.userStore.getUserById(userId);
|
|
4041
|
+
if (!user) {
|
|
4042
|
+
return reply.status(404).send({
|
|
4043
|
+
success: false,
|
|
4044
|
+
error: "User not found"
|
|
4045
|
+
});
|
|
4046
|
+
}
|
|
4047
|
+
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
4048
|
+
if (!tenant) {
|
|
4049
|
+
return reply.status(404).send({
|
|
4050
|
+
success: false,
|
|
4051
|
+
error: "Tenant not found"
|
|
4052
|
+
});
|
|
4053
|
+
}
|
|
4054
|
+
const link = await this.userTenantLinkStore.createLink({
|
|
4055
|
+
userId,
|
|
4056
|
+
tenantId,
|
|
4057
|
+
role
|
|
4058
|
+
});
|
|
4059
|
+
return {
|
|
4060
|
+
success: true,
|
|
4061
|
+
data: link
|
|
4062
|
+
};
|
|
4063
|
+
} catch (error) {
|
|
4064
|
+
console.error("Assign tenant error:", error);
|
|
4065
|
+
return reply.status(500).send({
|
|
4066
|
+
success: false,
|
|
4067
|
+
error: "Failed to assign tenant"
|
|
4068
|
+
});
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
async hashPassword(password) {
|
|
4072
|
+
const encoder = new TextEncoder();
|
|
4073
|
+
const data = encoder.encode(password + "salt");
|
|
4074
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
4075
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
4076
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4077
|
+
}
|
|
4078
|
+
async verifyPassword(password, hash) {
|
|
4079
|
+
const hashedPassword = await this.hashPassword(password);
|
|
4080
|
+
return hashedPassword === hash;
|
|
4081
|
+
}
|
|
4082
|
+
async generateToken(userId, tenantId) {
|
|
4083
|
+
const payload = {
|
|
4084
|
+
userId,
|
|
4085
|
+
exp: Date.now() + this.config.tokenExpiration * 1e3
|
|
4086
|
+
};
|
|
4087
|
+
if (tenantId) {
|
|
4088
|
+
payload.tenantId = tenantId;
|
|
4089
|
+
}
|
|
4090
|
+
return btoa(JSON.stringify(payload));
|
|
4091
|
+
}
|
|
4092
|
+
};
|
|
4093
|
+
function registerAuthRoutes(app2, config2) {
|
|
4094
|
+
const controller = new AuthController(config2);
|
|
4095
|
+
const authHook = async (request, reply) => {
|
|
4096
|
+
const authHeader = request.headers.authorization;
|
|
4097
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
4098
|
+
return reply.status(401).send({
|
|
4099
|
+
success: false,
|
|
4100
|
+
error: "Unauthorized - Missing or invalid token"
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
const token = authHeader.substring(7);
|
|
4104
|
+
try {
|
|
4105
|
+
const payload = JSON.parse(atob(token));
|
|
4106
|
+
if (payload.exp && payload.exp < Date.now()) {
|
|
4107
|
+
return reply.status(401).send({
|
|
4108
|
+
success: false,
|
|
4109
|
+
error: "Unauthorized - Token expired"
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
request.user = {
|
|
4113
|
+
id: payload.userId,
|
|
4114
|
+
tenantId: payload.tenantId
|
|
4115
|
+
};
|
|
4116
|
+
} catch {
|
|
4117
|
+
return reply.status(401).send({
|
|
4118
|
+
success: false,
|
|
4119
|
+
error: "Unauthorized - Invalid token"
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
};
|
|
4123
|
+
app2.post("/api/auth/register", controller.register.bind(controller));
|
|
4124
|
+
app2.post("/api/auth/login", controller.login.bind(controller));
|
|
4125
|
+
app2.get("/api/auth/tenants", { preHandler: authHook }, (req, res) => controller.getUserTenants(req, res));
|
|
4126
|
+
app2.post("/api/auth/select-tenant", { preHandler: authHook }, (req, res) => controller.selectTenant(req, res));
|
|
4127
|
+
app2.post("/api/auth/approve", { preHandler: authHook }, (req, res) => controller.approveUser(req, res));
|
|
4128
|
+
app2.get("/api/auth/pending", { preHandler: authHook }, (req, res) => controller.listPendingUsers(req, res));
|
|
4129
|
+
app2.post("/api/auth/assign-tenant", { preHandler: authHook }, (req, res) => controller.assignTenant(req, res));
|
|
4130
|
+
}
|
|
4131
|
+
|
|
3100
4132
|
// src/routes/index.ts
|
|
3101
4133
|
var registerLatticeRoutes = (app2) => {
|
|
3102
4134
|
app2.post("/api/runs", createRun);
|
|
@@ -3218,6 +4250,13 @@ var registerLatticeRoutes = (app2) => {
|
|
|
3218
4250
|
registerSandboxProxyRoutes(app2);
|
|
3219
4251
|
registerWorkspaceRoutes(app2);
|
|
3220
4252
|
registerDatabaseConfigRoutes(app2);
|
|
4253
|
+
registerMetricsServerConfigRoutes(app2);
|
|
4254
|
+
registerUserRoutes(app2);
|
|
4255
|
+
registerTenantRoutes(app2);
|
|
4256
|
+
registerAuthRoutes(app2, {
|
|
4257
|
+
autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
|
|
4258
|
+
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
|
|
4259
|
+
});
|
|
3221
4260
|
};
|
|
3222
4261
|
|
|
3223
4262
|
// src/swagger.ts
|
|
@@ -3283,7 +4322,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
3283
4322
|
};
|
|
3284
4323
|
|
|
3285
4324
|
// src/services/agent_task_consumer.ts
|
|
3286
|
-
var
|
|
4325
|
+
var import_core22 = require("@axiom-lattice/core");
|
|
3287
4326
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
3288
4327
|
const {
|
|
3289
4328
|
assistant_id,
|
|
@@ -3347,7 +4386,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
3347
4386
|
}
|
|
3348
4387
|
if (callback_event) {
|
|
3349
4388
|
const state = await agent_state({ assistant_id, thread_id });
|
|
3350
|
-
|
|
4389
|
+
import_core22.eventBus.publish(callback_event, {
|
|
3351
4390
|
success: true,
|
|
3352
4391
|
state,
|
|
3353
4392
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -3361,7 +4400,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
3361
4400
|
await response.text();
|
|
3362
4401
|
if (callback_event) {
|
|
3363
4402
|
const state = await agent_state({ assistant_id, thread_id });
|
|
3364
|
-
|
|
4403
|
+
import_core22.eventBus.publish(callback_event, {
|
|
3365
4404
|
success: true,
|
|
3366
4405
|
state,
|
|
3367
4406
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -3388,7 +4427,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
3388
4427
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
3389
4428
|
}
|
|
3390
4429
|
if (callback_event) {
|
|
3391
|
-
|
|
4430
|
+
import_core22.eventBus.publish(callback_event, {
|
|
3392
4431
|
success: false,
|
|
3393
4432
|
error: error instanceof Error ? error.message : String(error),
|
|
3394
4433
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -3426,7 +4465,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
3426
4465
|
* 初始化事件监听和队列轮询
|
|
3427
4466
|
*/
|
|
3428
4467
|
initialize() {
|
|
3429
|
-
|
|
4468
|
+
import_core22.eventBus.subscribe(import_core22.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
|
|
3430
4469
|
this.startPollingQueue();
|
|
3431
4470
|
console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
|
|
3432
4471
|
}
|
|
@@ -3545,7 +4584,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
3545
4584
|
handleAgentTask(taskRequest).catch((error) => {
|
|
3546
4585
|
console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
|
|
3547
4586
|
if (taskRequest.callback_event) {
|
|
3548
|
-
|
|
4587
|
+
import_core22.eventBus.publish(taskRequest.callback_event, {
|
|
3549
4588
|
success: false,
|
|
3550
4589
|
error: error instanceof Error ? error.message : String(error),
|
|
3551
4590
|
config: {
|
|
@@ -3565,7 +4604,7 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
|
|
|
3565
4604
|
var AgentTaskConsumer = _AgentTaskConsumer;
|
|
3566
4605
|
|
|
3567
4606
|
// src/index.ts
|
|
3568
|
-
var
|
|
4607
|
+
var import_core23 = require("@axiom-lattice/core");
|
|
3569
4608
|
var import_protocols2 = require("@axiom-lattice/protocols");
|
|
3570
4609
|
process.on("unhandledRejection", (reason, promise) => {
|
|
3571
4610
|
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
|
|
@@ -3580,11 +4619,11 @@ var DEFAULT_LOGGER_CONFIG = {
|
|
|
3580
4619
|
var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
|
|
3581
4620
|
var logger = loggerLattice.client;
|
|
3582
4621
|
function initializeLogger(config2) {
|
|
3583
|
-
if (
|
|
3584
|
-
|
|
4622
|
+
if (import_core23.loggerLatticeManager.hasLattice("default")) {
|
|
4623
|
+
import_core23.loggerLatticeManager.removeLattice("default");
|
|
3585
4624
|
}
|
|
3586
|
-
(0,
|
|
3587
|
-
return (0,
|
|
4625
|
+
(0, import_core23.registerLoggerLattice)("default", config2);
|
|
4626
|
+
return (0, import_core23.getLoggerLattice)("default");
|
|
3588
4627
|
}
|
|
3589
4628
|
var app = (0, import_fastify.default)({
|
|
3590
4629
|
logger: false,
|