@rebasepro/server-core 0.4.0 → 0.5.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/README.md +62 -25
- package/dist/common/src/util/permissions.d.ts +14 -6
- package/dist/index.es.js +75 -33
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +75 -33
- package/dist/index.umd.js.map +1 -1
- package/dist/server-core/src/api/errors.d.ts +15 -0
- package/dist/server-core/src/auth/jwt.d.ts +10 -0
- package/dist/types/src/types/backend.d.ts +36 -1
- package/dist/types/src/types/collections.d.ts +21 -1
- package/dist/types/src/types/properties.d.ts +0 -8
- package/package.json +5 -5
- package/src/api/errors.ts +20 -1
- package/src/api/openapi-generator.ts +1 -1
- package/src/api/rest/api-generator.ts +71 -15
- package/src/api/server.ts +1 -1
- package/src/auth/builtin-auth-adapter.ts +4 -15
- package/src/auth/jwt.ts +10 -0
- package/src/auth/routes.ts +5 -9
- package/src/init.ts +12 -20
- package/test.ts +0 -6
package/dist/index.umd.js
CHANGED
|
@@ -2777,6 +2777,9 @@
|
|
|
2777
2777
|
return new ApiError(503, code2, message);
|
|
2778
2778
|
}
|
|
2779
2779
|
}
|
|
2780
|
+
function isRebaseApiError(error2) {
|
|
2781
|
+
return error2 instanceof Error;
|
|
2782
|
+
}
|
|
2780
2783
|
const errorHandler = (err, c) => {
|
|
2781
2784
|
const error2 = err;
|
|
2782
2785
|
if (error2 instanceof ApiError || error2.name === "ApiError") {
|
|
@@ -3317,9 +3320,10 @@
|
|
|
3317
3320
|
}
|
|
3318
3321
|
return c.json(response, 201);
|
|
3319
3322
|
} catch (error2) {
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
+
if (isRebaseApiError(error2) && !error2.code) {
|
|
3324
|
+
error2.code = "BAD_REQUEST";
|
|
3325
|
+
}
|
|
3326
|
+
throw error2;
|
|
3323
3327
|
}
|
|
3324
3328
|
});
|
|
3325
3329
|
this.router.put(`${basePath}/:id`, async (c) => {
|
|
@@ -3355,9 +3359,10 @@
|
|
|
3355
3359
|
}
|
|
3356
3360
|
return c.json(response);
|
|
3357
3361
|
} catch (error2) {
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3362
|
+
if (isRebaseApiError(error2) && !error2.code) {
|
|
3363
|
+
error2.code = "BAD_REQUEST";
|
|
3364
|
+
}
|
|
3365
|
+
throw error2;
|
|
3361
3366
|
}
|
|
3362
3367
|
});
|
|
3363
3368
|
this.router.delete(`${basePath}/:id`, async (c) => {
|
|
@@ -3434,6 +3439,7 @@
|
|
|
3434
3439
|
if (!parsed) return next();
|
|
3435
3440
|
const driver = c.get("driver") || this.driver;
|
|
3436
3441
|
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3442
|
+
const hookCtx = this.buildHookContext(c, "GET");
|
|
3437
3443
|
if (parsed.entityId === "count") {
|
|
3438
3444
|
const queryDict = c.req.queries();
|
|
3439
3445
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3452,7 +3458,10 @@
|
|
|
3452
3458
|
entityId: parsed.entityId
|
|
3453
3459
|
});
|
|
3454
3460
|
if (!entity) throw ApiError.notFound("Entity not found");
|
|
3455
|
-
|
|
3461
|
+
const flatResult = this.flattenEntity(entity);
|
|
3462
|
+
const hookResult = await this.applyAfterRead(c.req.param("parent"), flatResult, hookCtx);
|
|
3463
|
+
if (!hookResult) throw ApiError.notFound("Entity not found");
|
|
3464
|
+
return c.json(hookResult);
|
|
3456
3465
|
} else {
|
|
3457
3466
|
const queryDict = c.req.queries();
|
|
3458
3467
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3465,13 +3474,20 @@
|
|
|
3465
3474
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3466
3475
|
searchString
|
|
3467
3476
|
});
|
|
3477
|
+
let flatEntities = entities.map((e) => this.flattenEntity(e));
|
|
3478
|
+
flatEntities = await this.applyAfterReadBatch(c.req.param("parent"), flatEntities, hookCtx);
|
|
3479
|
+
const total = driver.countEntities ? await driver.countEntities({
|
|
3480
|
+
path: parsed.collectionPath,
|
|
3481
|
+
filter: queryOptions.where,
|
|
3482
|
+
searchString
|
|
3483
|
+
}) : flatEntities.length;
|
|
3468
3484
|
return c.json({
|
|
3469
|
-
data:
|
|
3485
|
+
data: flatEntities,
|
|
3470
3486
|
meta: {
|
|
3471
|
-
total
|
|
3487
|
+
total,
|
|
3472
3488
|
limit: queryOptions.limit,
|
|
3473
3489
|
offset: queryOptions.offset,
|
|
3474
|
-
hasMore:
|
|
3490
|
+
hasMore: (queryOptions.offset || 0) + flatEntities.length < total
|
|
3475
3491
|
}
|
|
3476
3492
|
});
|
|
3477
3493
|
}
|
|
@@ -3483,14 +3499,24 @@
|
|
|
3483
3499
|
const parsed = parseSubPath(rawPath);
|
|
3484
3500
|
if (!parsed || parsed.entityId) return next();
|
|
3485
3501
|
const driver = c.get("driver") || this.driver;
|
|
3502
|
+
const hookCtx = this.buildHookContext(c, "POST");
|
|
3486
3503
|
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3487
|
-
|
|
3504
|
+
let body = await c.req.json().catch(() => ({}));
|
|
3505
|
+
if (this.dataHooks?.beforeSave) {
|
|
3506
|
+
body = await this.dataHooks.beforeSave(parsed.collectionPath, body, void 0, hookCtx);
|
|
3507
|
+
}
|
|
3488
3508
|
const entity = await driver.saveEntity({
|
|
3489
3509
|
path: parsed.collectionPath,
|
|
3490
3510
|
values: body,
|
|
3491
3511
|
status: "new"
|
|
3492
3512
|
});
|
|
3493
|
-
|
|
3513
|
+
const response = this.formatResponse(entity);
|
|
3514
|
+
if (this.dataHooks?.afterSave) {
|
|
3515
|
+
Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response, hookCtx)).catch((err) => {
|
|
3516
|
+
console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
return c.json(response, 201);
|
|
3494
3520
|
});
|
|
3495
3521
|
this.router.put("/:parent/:parentId/:rest{.+}", async (c, next) => {
|
|
3496
3522
|
const rest = c.req.param("rest");
|
|
@@ -3499,15 +3525,25 @@
|
|
|
3499
3525
|
const parsed = parseSubPath(rawPath);
|
|
3500
3526
|
if (!parsed || !parsed.entityId) return next();
|
|
3501
3527
|
const driver = c.get("driver") || this.driver;
|
|
3528
|
+
const hookCtx = this.buildHookContext(c, "PUT");
|
|
3502
3529
|
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3503
|
-
|
|
3530
|
+
let body = await c.req.json().catch(() => ({}));
|
|
3531
|
+
if (this.dataHooks?.beforeSave) {
|
|
3532
|
+
body = await this.dataHooks.beforeSave(parsed.collectionPath, body, parsed.entityId, hookCtx);
|
|
3533
|
+
}
|
|
3504
3534
|
const entity = await driver.saveEntity({
|
|
3505
3535
|
path: parsed.collectionPath,
|
|
3506
3536
|
entityId: parsed.entityId,
|
|
3507
3537
|
values: body,
|
|
3508
3538
|
status: "existing"
|
|
3509
3539
|
});
|
|
3510
|
-
|
|
3540
|
+
const response = this.formatResponse(entity);
|
|
3541
|
+
if (this.dataHooks?.afterSave) {
|
|
3542
|
+
Promise.resolve(this.dataHooks.afterSave(parsed.collectionPath, response, hookCtx)).catch((err) => {
|
|
3543
|
+
console.error("[BackendHooks] data.afterSave error:", err instanceof Error ? err.message : err);
|
|
3544
|
+
});
|
|
3545
|
+
}
|
|
3546
|
+
return c.json(response);
|
|
3511
3547
|
});
|
|
3512
3548
|
this.router.delete("/:parent/:parentId/:rest{.+}", async (c, next) => {
|
|
3513
3549
|
const rest = c.req.param("rest");
|
|
@@ -3516,15 +3552,24 @@
|
|
|
3516
3552
|
const parsed = parseSubPath(rawPath);
|
|
3517
3553
|
if (!parsed || !parsed.entityId) return next();
|
|
3518
3554
|
const driver = c.get("driver") || this.driver;
|
|
3555
|
+
const hookCtx = this.buildHookContext(c, "DELETE");
|
|
3519
3556
|
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3520
3557
|
const existingEntity = await driver.fetchEntity({
|
|
3521
3558
|
path: parsed.collectionPath,
|
|
3522
3559
|
entityId: parsed.entityId
|
|
3523
3560
|
});
|
|
3524
3561
|
if (!existingEntity) throw ApiError.notFound("Entity not found");
|
|
3562
|
+
if (this.dataHooks?.beforeDelete) {
|
|
3563
|
+
await this.dataHooks.beforeDelete(parsed.collectionPath, parsed.entityId, hookCtx);
|
|
3564
|
+
}
|
|
3525
3565
|
await driver.deleteEntity({
|
|
3526
3566
|
entity: existingEntity
|
|
3527
3567
|
});
|
|
3568
|
+
if (this.dataHooks?.afterDelete) {
|
|
3569
|
+
Promise.resolve(this.dataHooks.afterDelete(parsed.collectionPath, parsed.entityId, hookCtx)).catch((err) => {
|
|
3570
|
+
console.error("[BackendHooks] data.afterDelete error:", err instanceof Error ? err.message : err);
|
|
3571
|
+
});
|
|
3572
|
+
}
|
|
3528
3573
|
return new Response(null, {
|
|
3529
3574
|
status: 204
|
|
3530
3575
|
});
|
|
@@ -13007,14 +13052,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13007
13052
|
if (!factor || factor.userId !== userCtx.userId) {
|
|
13008
13053
|
throw ApiError.notFound("MFA factor not found");
|
|
13009
13054
|
}
|
|
13010
|
-
const
|
|
13011
|
-
let isValid2 =
|
|
13012
|
-
if (
|
|
13055
|
+
const secretBuffer = base32Decode(factor.secretEncrypted);
|
|
13056
|
+
let isValid2 = verifyTotp(secretBuffer, code2);
|
|
13057
|
+
if (!isValid2) {
|
|
13013
13058
|
const codeHash = hashRecoveryCode(code2);
|
|
13014
13059
|
isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
|
|
13015
|
-
} else {
|
|
13016
|
-
const secretBuffer = base32Decode(factor.secretEncrypted);
|
|
13017
|
-
isValid2 = verifyTotp(secretBuffer, code2);
|
|
13018
13060
|
}
|
|
13019
13061
|
if (!isValid2) {
|
|
13020
13062
|
throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
|
|
@@ -13530,7 +13572,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13530
13572
|
if (!payload) {
|
|
13531
13573
|
return null;
|
|
13532
13574
|
}
|
|
13533
|
-
const extendedPayload = payload;
|
|
13534
13575
|
let roles = payload.roles || [];
|
|
13535
13576
|
try {
|
|
13536
13577
|
roles = await authRepository.getUserRoleIds(payload.userId);
|
|
@@ -13539,8 +13580,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13539
13580
|
const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
|
|
13540
13581
|
return {
|
|
13541
13582
|
uid: payload.userId,
|
|
13542
|
-
email:
|
|
13543
|
-
displayName:
|
|
13583
|
+
email: payload.email ?? "",
|
|
13584
|
+
displayName: payload.displayName ?? null,
|
|
13544
13585
|
roles,
|
|
13545
13586
|
isAdmin,
|
|
13546
13587
|
rawToken: token
|
|
@@ -13560,7 +13601,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13560
13601
|
if (!payload) {
|
|
13561
13602
|
return null;
|
|
13562
13603
|
}
|
|
13563
|
-
const extendedPayload = payload;
|
|
13564
13604
|
let roles = payload.roles || [];
|
|
13565
13605
|
try {
|
|
13566
13606
|
roles = await authRepository.getUserRoleIds(payload.userId);
|
|
@@ -13569,8 +13609,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13569
13609
|
const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
|
|
13570
13610
|
return {
|
|
13571
13611
|
uid: payload.userId,
|
|
13572
|
-
email:
|
|
13573
|
-
displayName:
|
|
13612
|
+
email: payload.email ?? "",
|
|
13613
|
+
displayName: payload.displayName ?? null,
|
|
13574
13614
|
roles,
|
|
13575
13615
|
isAdmin,
|
|
13576
13616
|
rawToken: token
|
|
@@ -38609,7 +38649,9 @@ ${credentialScope}
|
|
|
38609
38649
|
}
|
|
38610
38650
|
if (bootstrapper.initializeRealtime) {
|
|
38611
38651
|
const realtime = await bootstrapper.initializeRealtime({}, driverResult);
|
|
38612
|
-
|
|
38652
|
+
if (realtime) {
|
|
38653
|
+
realtimeServices[b.id || bootstrapper.type] = realtime;
|
|
38654
|
+
}
|
|
38613
38655
|
}
|
|
38614
38656
|
}
|
|
38615
38657
|
const driverRegistry = DefaultDriverRegistry.create(delegates);
|
|
@@ -39088,7 +39130,7 @@ ${credentialScope}
|
|
|
39088
39130
|
});
|
|
39089
39131
|
}
|
|
39090
39132
|
}
|
|
39091
|
-
if (defaultBootstrapper.initializeWebsockets) {
|
|
39133
|
+
if (defaultBootstrapper.initializeWebsockets && defaultRealtimeService) {
|
|
39092
39134
|
await defaultBootstrapper.initializeWebsockets(config.server, defaultRealtimeService, defaultDriver, config.auth, authAdapter);
|
|
39093
39135
|
}
|
|
39094
39136
|
logger.info("Rebase Backend Initialized");
|
|
@@ -39134,12 +39176,11 @@ ${credentialScope}
|
|
|
39134
39176
|
}
|
|
39135
39177
|
for (const [key, rt] of Object.entries(realtimeServices)) {
|
|
39136
39178
|
try {
|
|
39137
|
-
|
|
39138
|
-
|
|
39139
|
-
await rtWithLifecycle.destroy();
|
|
39179
|
+
if (typeof rt.destroy === "function") {
|
|
39180
|
+
await rt.destroy();
|
|
39140
39181
|
logger.info(`Realtime service "${key}" destroyed`);
|
|
39141
|
-
} else if (typeof
|
|
39142
|
-
await
|
|
39182
|
+
} else if (typeof rt.stopListening === "function") {
|
|
39183
|
+
await rt.stopListening();
|
|
39143
39184
|
logger.info(`Realtime service "${key}" LISTEN client stopped`);
|
|
39144
39185
|
}
|
|
39145
39186
|
} catch (err) {
|
|
@@ -51293,6 +51334,7 @@ export default ${safeId}Collection;
|
|
|
51293
51334
|
exports2.isAuthAdapter = isAuthAdapter;
|
|
51294
51335
|
exports2.isDatabaseAdapter = isDatabaseAdapter;
|
|
51295
51336
|
exports2.isOperationAllowed = isOperationAllowed;
|
|
51337
|
+
exports2.isRebaseApiError = isRebaseApiError;
|
|
51296
51338
|
exports2.isTransformableImage = isTransformableImage;
|
|
51297
51339
|
exports2.listenWithPortRetry = listenWithPortRetry;
|
|
51298
51340
|
exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
|