@rebasepro/server-core 0.2.3 → 0.2.5
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/dist/common/src/collections/default-collections.d.ts +9 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/util/permissions.d.ts +1 -0
- package/dist/{index-BZoAtuqi.js → index-Cr1D21av.js} +11 -3
- package/dist/index-Cr1D21av.js.map +1 -0
- package/dist/index.es.js +2309 -370
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2298 -355
- package/dist/index.umd.js.map +1 -1
- package/dist/server-core/src/api/logs-routes.d.ts +37 -0
- package/dist/server-core/src/api/rest/api-generator.d.ts +6 -0
- package/dist/server-core/src/api/types.d.ts +6 -1
- package/dist/server-core/src/auth/adapter-middleware.d.ts +7 -3
- package/dist/server-core/src/auth/admin-routes.d.ts +3 -3
- package/dist/server-core/src/auth/api-keys/api-key-middleware.d.ts +39 -0
- package/dist/server-core/src/auth/api-keys/api-key-permission-guard.d.ts +32 -0
- package/dist/server-core/src/auth/api-keys/api-key-routes.d.ts +20 -0
- package/dist/server-core/src/auth/api-keys/api-key-store.d.ts +35 -0
- package/dist/server-core/src/auth/api-keys/api-key-types.d.ts +88 -0
- package/dist/server-core/src/auth/api-keys/index.d.ts +17 -0
- package/dist/server-core/src/auth/{auth-overrides.d.ts → auth-hooks.d.ts} +69 -11
- package/dist/server-core/src/auth/builtin-auth-adapter.d.ts +3 -3
- package/dist/server-core/src/auth/index.d.ts +5 -3
- package/dist/server-core/src/auth/interfaces.d.ts +93 -3
- package/dist/server-core/src/auth/jwt.d.ts +3 -1
- package/dist/server-core/src/auth/mfa.d.ts +49 -0
- package/dist/server-core/src/auth/middleware.d.ts +7 -0
- package/dist/server-core/src/auth/rate-limiter.d.ts +19 -0
- package/dist/server-core/src/auth/routes.d.ts +3 -3
- package/dist/server-core/src/env.d.ts +6 -0
- package/dist/server-core/src/index.d.ts +1 -0
- package/dist/server-core/src/init.d.ts +3 -3
- package/dist/server-core/src/services/webhook-service.d.ts +29 -0
- package/dist/server-core/src/storage/image-transform.d.ts +48 -0
- package/dist/server-core/src/storage/index.d.ts +3 -0
- package/dist/server-core/src/storage/tus-handler.d.ts +51 -0
- package/dist/types/src/controllers/auth.d.ts +4 -26
- package/dist/types/src/controllers/client.d.ts +25 -43
- package/dist/types/src/controllers/collection_registry.d.ts +1 -1
- package/dist/types/src/controllers/data.d.ts +4 -0
- package/dist/types/src/controllers/data_driver.d.ts +23 -0
- package/dist/types/src/controllers/registry.d.ts +5 -4
- package/dist/types/src/rebase_context.d.ts +1 -1
- package/dist/types/src/types/auth_adapter.d.ts +5 -60
- package/dist/types/src/types/backend.d.ts +2 -2
- package/dist/types/src/types/backend_hooks.d.ts +2 -17
- package/dist/types/src/types/collections.d.ts +0 -4
- package/dist/types/src/types/component_ref.d.ts +1 -1
- package/dist/types/src/types/cron.d.ts +1 -1
- package/dist/types/src/types/entity_views.d.ts +1 -0
- package/dist/types/src/types/export_import.d.ts +1 -1
- package/dist/types/src/types/formex.d.ts +2 -2
- package/dist/types/src/types/properties.d.ts +9 -7
- package/dist/types/src/types/translations.d.ts +28 -12
- package/dist/types/src/types/user_management_delegate.d.ts +22 -57
- package/dist/types/src/users/index.d.ts +0 -1
- package/dist/types/src/users/user.d.ts +0 -1
- package/package.json +8 -6
- package/src/api/ast-schema-editor.ts +4 -4
- package/src/api/errors.ts +16 -7
- package/src/api/logs-routes.ts +129 -0
- package/src/api/rest/api-generator.ts +42 -2
- package/src/api/rest/query-parser.ts +37 -1
- package/src/api/types.ts +6 -1
- package/src/auth/adapter-middleware.ts +20 -4
- package/src/auth/admin-routes.ts +36 -100
- package/src/auth/api-keys/api-key-middleware.ts +126 -0
- package/src/auth/api-keys/api-key-permission-guard.ts +64 -0
- package/src/auth/api-keys/api-key-routes.ts +183 -0
- package/src/auth/api-keys/api-key-store.ts +317 -0
- package/src/auth/api-keys/api-key-types.ts +94 -0
- package/src/auth/api-keys/index.ts +37 -0
- package/src/auth/{auth-overrides.ts → auth-hooks.ts} +81 -14
- package/src/auth/builtin-auth-adapter.ts +35 -73
- package/src/auth/custom-auth-adapter.ts +1 -1
- package/src/auth/index.ts +7 -3
- package/src/auth/interfaces.ts +111 -3
- package/src/auth/jwt.ts +19 -5
- package/src/auth/mfa.ts +160 -0
- package/src/auth/middleware.ts +20 -1
- package/src/auth/rate-limiter.ts +92 -0
- package/src/auth/routes.ts +455 -24
- package/src/cron/cron-loader.ts +5 -10
- package/src/cron/cron-scheduler.ts +11 -12
- package/src/cron/cron-store.ts +8 -7
- package/src/env.ts +2 -0
- package/src/functions/function-loader.ts +6 -9
- package/src/index.ts +1 -2
- package/src/init.ts +56 -8
- package/src/serve-spa.ts +5 -4
- package/src/services/webhook-service.ts +155 -0
- package/src/storage/image-transform.ts +202 -0
- package/src/storage/index.ts +3 -0
- package/src/storage/routes.ts +56 -3
- package/src/storage/tus-handler.ts +315 -0
- package/src/utils/dev-port.ts +14 -0
- package/src/utils/logging.ts +9 -7
- package/test/admin-routes.test.ts +62 -164
- package/test/api-generator.test.ts +0 -1
- package/test/api-key-permission-guard.test.ts +132 -0
- package/test/ast-schema-editor.test.ts +26 -0
- package/test/auth-routes.test.ts +1 -2
- package/test/backend-hooks-admin.test.ts +3 -29
- package/test/custom-auth-adapter.test.ts +2 -10
- package/test/email-templates.test.ts +169 -0
- package/test/function-loader.test.ts +124 -0
- package/test/jwt.test.ts +4 -2
- package/test/mfa.test.ts +197 -0
- package/test/middleware.test.ts +10 -5
- package/test/webhook-service.test.ts +249 -0
- package/vite.config.ts +3 -2
- package/dist/index-BZoAtuqi.js.map +0 -1
- package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
- package/dist/types/src/users/roles.d.ts +0 -22
- package/src/bootstrappers/index.ts +0 -1
- package/src/singleton.test.ts +0 -28
package/dist/index.umd.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function(global2, factory) {
|
|
2
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("fs"), require("path"), require("url"), require("hono"), require("buffer"), require("stream"), require("util"), require("crypto"), require("hono/body-limit"), require("hono/csrf"), require("child_process"), require("https"), require("net"), require("tls"), require("assert"), require("http"), require("os"), require("node:events"), require("node:process"), require("node:util"), require("events"), require("@aws-sdk/client-s3"), require("@aws-sdk/s3-request-presigner"), require("hono/cors"), require("hono/secure-headers"), require("@hono/node-server"), require("ts-morph"), require("drizzle-orm"), require("@hono/node-server/serve-static")) : typeof define === "function" && define.amd ? define(["exports", "fs", "path", "url", "hono", "buffer", "stream", "util", "crypto", "hono/body-limit", "hono/csrf", "child_process", "https", "net", "tls", "assert", "http", "os", "node:events", "node:process", "node:util", "events", "@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner", "hono/cors", "hono/secure-headers", "@hono/node-server", "ts-morph", "drizzle-orm", "@hono/node-server/serve-static"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2["Rebase Backend"] = {}, global2.fs$4, global2.path$3, global2.require$$0$2, global2.hono, global2.require$$0$3, global2.require$$0$4, global2.require$$5, global2.require$$0$5, global2.bodyLimit, global2.csrf, global2.require$$0$a, global2.require$$1, global2.require$$0$7, global2.require$$1$1, global2.require$$2, global2.require$$0$6, global2.require$$1$
|
|
3
|
-
})(this, function(exports2, fs$4, path$3, require$$0$2, hono, require$$0$3, require$$0$4, require$$5, require$$0$5, bodyLimit, csrf, require$$0$a, require$$1, require$$0$7, require$$1$1, require$$2, require$$0$6, require$$1$
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("fs"), require("path"), require("url"), require("hono"), require("buffer"), require("stream"), require("util"), require("crypto"), require("hono/body-limit"), require("hono/csrf"), require("child_process"), require("https"), require("querystring"), require("net"), require("tls"), require("assert"), require("http"), require("os"), require("node:events"), require("node:process"), require("node:util"), require("events"), require("@aws-sdk/client-s3"), require("@aws-sdk/s3-request-presigner"), require("fs/promises"), require("zlib"), require("dns"), require("hono/cors"), require("hono/secure-headers"), require("@hono/node-server"), require("ts-morph"), require("drizzle-orm"), require("@hono/node-server/serve-static")) : typeof define === "function" && define.amd ? define(["exports", "fs", "path", "url", "hono", "buffer", "stream", "util", "crypto", "hono/body-limit", "hono/csrf", "child_process", "https", "querystring", "net", "tls", "assert", "http", "os", "node:events", "node:process", "node:util", "events", "@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner", "fs/promises", "zlib", "dns", "hono/cors", "hono/secure-headers", "@hono/node-server", "ts-morph", "drizzle-orm", "@hono/node-server/serve-static"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2["Rebase Backend"] = {}, global2.fs$4, global2.path$3, global2.require$$0$2, global2.hono, global2.require$$0$3, global2.require$$0$4, global2.require$$5, global2.require$$0$5, global2.bodyLimit, global2.csrf, global2.require$$0$a, global2.require$$1, global2.require$$1$2, global2.require$$0$7, global2.require$$1$1, global2.require$$2, global2.require$$0$6, global2.require$$1$3, global2.require$$0$8, global2.require$$1$4, global2.require$$2$1, global2.require$$0$9, global2.clientS3, global2.s3RequestPresigner, global2.promises, global2.require$$3, global2.require$$4$1, global2.cors, global2.secureHeaders, global2.nodeServer, global2.tsMorph, global2.drizzleOrm, global2.serveStatic));
|
|
3
|
+
})(this, function(exports2, fs$4, path$3, require$$0$2, hono, require$$0$3, require$$0$4, require$$5, require$$0$5, bodyLimit, csrf, require$$0$a, require$$1, require$$1$2, require$$0$7, require$$1$1, require$$2, require$$0$6, require$$1$3, require$$0$8, require$$1$4, require$$2$1, require$$0$9, clientS3, s3RequestPresigner, promises, require$$3, require$$4$1, cors, secureHeaders, nodeServer, tsMorph, drizzleOrm, serveStatic) {
|
|
4
4
|
"use strict";
|
|
5
5
|
function _interopNamespaceDefault(e) {
|
|
6
6
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -202,30 +202,6 @@
|
|
|
202
202
|
function getDefaultExportFromCjs(x) {
|
|
203
203
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
204
204
|
}
|
|
205
|
-
function getAugmentedNamespace(n) {
|
|
206
|
-
if (n.__esModule) return n;
|
|
207
|
-
var f2 = n.default;
|
|
208
|
-
if (typeof f2 == "function") {
|
|
209
|
-
var a2 = function a3() {
|
|
210
|
-
if (this instanceof a3) {
|
|
211
|
-
return Reflect.construct(f2, arguments, this.constructor);
|
|
212
|
-
}
|
|
213
|
-
return f2.apply(this, arguments);
|
|
214
|
-
};
|
|
215
|
-
a2.prototype = f2.prototype;
|
|
216
|
-
} else a2 = {};
|
|
217
|
-
Object.defineProperty(a2, "__esModule", { value: true });
|
|
218
|
-
Object.keys(n).forEach(function(k) {
|
|
219
|
-
var d2 = Object.getOwnPropertyDescriptor(n, k);
|
|
220
|
-
Object.defineProperty(a2, k, d2.get ? d2 : {
|
|
221
|
-
enumerable: true,
|
|
222
|
-
get: function() {
|
|
223
|
-
return n[k];
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
return a2;
|
|
228
|
-
}
|
|
229
205
|
function commonjsRequire(path2) {
|
|
230
206
|
throw new Error('Could not dynamically require "' + path2 + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
|
|
231
207
|
}
|
|
@@ -1057,6 +1033,9 @@
|
|
|
1057
1033
|
return output;
|
|
1058
1034
|
}
|
|
1059
1035
|
for (const key in source) {
|
|
1036
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1060
1039
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
1061
1040
|
const sourceValue = source[key];
|
|
1062
1041
|
const outputValue = output[key];
|
|
@@ -1245,8 +1224,8 @@
|
|
|
1245
1224
|
} catch (e) {
|
|
1246
1225
|
}
|
|
1247
1226
|
if (!foundForeignKey) {
|
|
1248
|
-
const
|
|
1249
|
-
newRelation.foreignKeyOnTarget = generateForeignKeyName(
|
|
1227
|
+
const keyPrefix2 = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
|
|
1228
|
+
newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix2);
|
|
1250
1229
|
}
|
|
1251
1230
|
}
|
|
1252
1231
|
} else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
|
|
@@ -2555,6 +2534,142 @@
|
|
|
2555
2534
|
};
|
|
2556
2535
|
}
|
|
2557
2536
|
}
|
|
2537
|
+
const defaultUsersCollection = {
|
|
2538
|
+
name: "Users",
|
|
2539
|
+
singularName: "User",
|
|
2540
|
+
slug: "users",
|
|
2541
|
+
table: "users",
|
|
2542
|
+
schema: "rebase",
|
|
2543
|
+
icon: "Users",
|
|
2544
|
+
group: "Settings",
|
|
2545
|
+
openEntityMode: "dialog",
|
|
2546
|
+
disableDefaultActions: ["copy"],
|
|
2547
|
+
sort: ["createdAt", "desc"],
|
|
2548
|
+
properties: {
|
|
2549
|
+
id: {
|
|
2550
|
+
name: "ID",
|
|
2551
|
+
type: "string",
|
|
2552
|
+
isId: "uuid",
|
|
2553
|
+
ui: {
|
|
2554
|
+
readOnly: true
|
|
2555
|
+
}
|
|
2556
|
+
},
|
|
2557
|
+
email: {
|
|
2558
|
+
name: "Email",
|
|
2559
|
+
type: "string",
|
|
2560
|
+
validation: {
|
|
2561
|
+
required: true,
|
|
2562
|
+
unique: true
|
|
2563
|
+
}
|
|
2564
|
+
},
|
|
2565
|
+
displayName: {
|
|
2566
|
+
name: "Name",
|
|
2567
|
+
type: "string",
|
|
2568
|
+
columnName: "display_name",
|
|
2569
|
+
validation: {
|
|
2570
|
+
required: true
|
|
2571
|
+
}
|
|
2572
|
+
},
|
|
2573
|
+
photoURL: {
|
|
2574
|
+
name: "Photo URL",
|
|
2575
|
+
type: "string",
|
|
2576
|
+
columnName: "photo_url",
|
|
2577
|
+
url: "image"
|
|
2578
|
+
},
|
|
2579
|
+
roles: {
|
|
2580
|
+
name: "Roles",
|
|
2581
|
+
type: "array",
|
|
2582
|
+
columnType: "text[]",
|
|
2583
|
+
of: {
|
|
2584
|
+
name: "Role",
|
|
2585
|
+
type: "string",
|
|
2586
|
+
enum: {
|
|
2587
|
+
admin: "Admin",
|
|
2588
|
+
editor: "Editor",
|
|
2589
|
+
viewer: "Viewer"
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
},
|
|
2593
|
+
passwordHash: {
|
|
2594
|
+
name: "Password Hash",
|
|
2595
|
+
type: "string",
|
|
2596
|
+
columnName: "password_hash",
|
|
2597
|
+
ui: {
|
|
2598
|
+
hideFromCollection: true,
|
|
2599
|
+
disabled: {
|
|
2600
|
+
hidden: true
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
},
|
|
2604
|
+
emailVerified: {
|
|
2605
|
+
name: "Email Verified",
|
|
2606
|
+
type: "boolean",
|
|
2607
|
+
columnName: "email_verified",
|
|
2608
|
+
defaultValue: false,
|
|
2609
|
+
ui: {
|
|
2610
|
+
hideFromCollection: true,
|
|
2611
|
+
disabled: {
|
|
2612
|
+
hidden: true
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
},
|
|
2616
|
+
emailVerificationToken: {
|
|
2617
|
+
name: "Email Verification Token",
|
|
2618
|
+
type: "string",
|
|
2619
|
+
columnName: "email_verification_token",
|
|
2620
|
+
ui: {
|
|
2621
|
+
hideFromCollection: true,
|
|
2622
|
+
disabled: {
|
|
2623
|
+
hidden: true
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
},
|
|
2627
|
+
emailVerificationSentAt: {
|
|
2628
|
+
name: "Email Verification Sent At",
|
|
2629
|
+
type: "date",
|
|
2630
|
+
columnName: "email_verification_sent_at",
|
|
2631
|
+
ui: {
|
|
2632
|
+
hideFromCollection: true,
|
|
2633
|
+
disabled: {
|
|
2634
|
+
hidden: true
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
},
|
|
2638
|
+
metadata: {
|
|
2639
|
+
name: "Metadata",
|
|
2640
|
+
type: "map",
|
|
2641
|
+
defaultValue: {},
|
|
2642
|
+
ui: {
|
|
2643
|
+
hideFromCollection: true,
|
|
2644
|
+
disabled: {
|
|
2645
|
+
hidden: true
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
},
|
|
2649
|
+
createdAt: {
|
|
2650
|
+
name: "Created At",
|
|
2651
|
+
type: "date",
|
|
2652
|
+
columnName: "created_at",
|
|
2653
|
+
ui: {
|
|
2654
|
+
readOnly: true
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2657
|
+
updatedAt: {
|
|
2658
|
+
name: "Updated At",
|
|
2659
|
+
type: "date",
|
|
2660
|
+
columnName: "updated_at",
|
|
2661
|
+
autoValue: "on_update",
|
|
2662
|
+
ui: {
|
|
2663
|
+
hideFromCollection: true,
|
|
2664
|
+
disabled: {
|
|
2665
|
+
hidden: true
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
},
|
|
2670
|
+
listProperties: ["displayName", "email", "roles", "createdAt"],
|
|
2671
|
+
propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
|
|
2672
|
+
};
|
|
2558
2673
|
function mapOperator$1(op) {
|
|
2559
2674
|
switch (op) {
|
|
2560
2675
|
case "==":
|
|
@@ -2846,10 +2961,15 @@
|
|
|
2846
2961
|
const issue = error2.code === "42703" ? "column" : "table";
|
|
2847
2962
|
logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
|
|
2848
2963
|
}
|
|
2849
|
-
console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2850
2964
|
const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
|
|
2851
2965
|
const pgErrorCode = causePg?.code || error2.code;
|
|
2852
|
-
const
|
|
2966
|
+
const isDbSchemaMismatch = pgErrorCode === "42703" || pgErrorCode === "42P01";
|
|
2967
|
+
if (isDbSchemaMismatch) {
|
|
2968
|
+
console.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2969
|
+
} else {
|
|
2970
|
+
console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2971
|
+
}
|
|
2972
|
+
const suppressStack = isDbSchemaMismatch || statusCode < 500 && code2 === "BAD_REQUEST";
|
|
2853
2973
|
if (!suppressStack) {
|
|
2854
2974
|
console.error(error2.stack || error2);
|
|
2855
2975
|
}
|
|
@@ -2930,7 +3050,7 @@
|
|
|
2930
3050
|
options2.offset = (page - 1) * limit;
|
|
2931
3051
|
}
|
|
2932
3052
|
options2.where = {};
|
|
2933
|
-
const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString"];
|
|
3053
|
+
const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold"];
|
|
2934
3054
|
for (const [key, rawValue] of Object.entries(query)) {
|
|
2935
3055
|
if (reservedQueryKeys.includes(key)) continue;
|
|
2936
3056
|
const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
|
|
@@ -3002,8 +3122,63 @@
|
|
|
3002
3122
|
const fieldsStr = String(query.fields).trim();
|
|
3003
3123
|
options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
3004
3124
|
}
|
|
3125
|
+
if (query.vector_search && query.vector) {
|
|
3126
|
+
const vectorStr = String(query.vector);
|
|
3127
|
+
let queryVector;
|
|
3128
|
+
try {
|
|
3129
|
+
queryVector = JSON.parse(vectorStr);
|
|
3130
|
+
if (!Array.isArray(queryVector) || !queryVector.every((v) => typeof v === "number")) {
|
|
3131
|
+
throw new Error("Expected array of numbers");
|
|
3132
|
+
}
|
|
3133
|
+
} catch {
|
|
3134
|
+
throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
|
|
3135
|
+
}
|
|
3136
|
+
const distanceParam = query.vector_distance ? String(query.vector_distance) : "cosine";
|
|
3137
|
+
if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
|
|
3138
|
+
throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
|
|
3139
|
+
}
|
|
3140
|
+
const vectorSearch = {
|
|
3141
|
+
property: String(query.vector_search),
|
|
3142
|
+
vector: queryVector,
|
|
3143
|
+
distance: distanceParam
|
|
3144
|
+
};
|
|
3145
|
+
if (query.vector_threshold) {
|
|
3146
|
+
const threshold = parseFloat(String(query.vector_threshold));
|
|
3147
|
+
if (isNaN(threshold)) {
|
|
3148
|
+
throw new Error("Invalid vector_threshold. Expected a number.");
|
|
3149
|
+
}
|
|
3150
|
+
vectorSearch.threshold = threshold;
|
|
3151
|
+
}
|
|
3152
|
+
options2.vectorSearch = vectorSearch;
|
|
3153
|
+
}
|
|
3005
3154
|
return options2;
|
|
3006
3155
|
}
|
|
3156
|
+
function httpMethodToOperation(method) {
|
|
3157
|
+
const upper = method.toUpperCase();
|
|
3158
|
+
switch (upper) {
|
|
3159
|
+
case "GET":
|
|
3160
|
+
case "HEAD":
|
|
3161
|
+
case "OPTIONS":
|
|
3162
|
+
return "read";
|
|
3163
|
+
case "POST":
|
|
3164
|
+
case "PUT":
|
|
3165
|
+
case "PATCH":
|
|
3166
|
+
return "write";
|
|
3167
|
+
case "DELETE":
|
|
3168
|
+
return "delete";
|
|
3169
|
+
default:
|
|
3170
|
+
return "read";
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
function isOperationAllowed(permissions, collection, operation) {
|
|
3174
|
+
for (const perm of permissions) {
|
|
3175
|
+
const collectionMatch = perm.collection === "*" || perm.collection === collection;
|
|
3176
|
+
if (collectionMatch && perm.operations.includes(operation)) {
|
|
3177
|
+
return true;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
return false;
|
|
3181
|
+
}
|
|
3007
3182
|
class RestApiGenerator {
|
|
3008
3183
|
collections;
|
|
3009
3184
|
router;
|
|
@@ -3036,6 +3211,19 @@
|
|
|
3036
3211
|
this.createSubcollectionRoutes();
|
|
3037
3212
|
return this.router;
|
|
3038
3213
|
}
|
|
3214
|
+
/**
|
|
3215
|
+
* Check API key permissions for a collection operation.
|
|
3216
|
+
* Throws 403 if the key doesn't have the required permission.
|
|
3217
|
+
* No-ops if the request is not authenticated via an API key.
|
|
3218
|
+
*/
|
|
3219
|
+
enforceApiKeyPermission(c, collectionSlug) {
|
|
3220
|
+
const apiKey = c.get("apiKey");
|
|
3221
|
+
if (!apiKey) return;
|
|
3222
|
+
const operation = httpMethodToOperation(c.req.method);
|
|
3223
|
+
if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
|
|
3224
|
+
throw ApiError.forbidden(`API key does not have "${operation}" permission for collection "${collectionSlug}"`, "API_KEY_FORBIDDEN");
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3039
3227
|
/**
|
|
3040
3228
|
* Get the typed RestFetchService from a driver if it exposes one (for include support).
|
|
3041
3229
|
*/
|
|
@@ -3049,6 +3237,7 @@
|
|
|
3049
3237
|
const basePath = `/${collection.slug}`;
|
|
3050
3238
|
const resolvedCollection = collection;
|
|
3051
3239
|
this.router.get(`${basePath}/count`, async (c) => {
|
|
3240
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3052
3241
|
const queryDict = c.req.query();
|
|
3053
3242
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3054
3243
|
const searchString = queryDict.searchString;
|
|
@@ -3059,6 +3248,7 @@
|
|
|
3059
3248
|
});
|
|
3060
3249
|
});
|
|
3061
3250
|
this.router.get(basePath, async (c) => {
|
|
3251
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3062
3252
|
const queryDict = c.req.query();
|
|
3063
3253
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3064
3254
|
const searchString = queryDict.searchString;
|
|
@@ -3073,7 +3263,8 @@
|
|
|
3073
3263
|
offset: queryOptions.offset,
|
|
3074
3264
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
3075
3265
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3076
|
-
searchString
|
|
3266
|
+
searchString,
|
|
3267
|
+
vectorSearch: queryOptions.vectorSearch
|
|
3077
3268
|
}, queryOptions.include);
|
|
3078
3269
|
entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
|
|
3079
3270
|
const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
|
|
@@ -3101,6 +3292,7 @@
|
|
|
3101
3292
|
});
|
|
3102
3293
|
});
|
|
3103
3294
|
this.router.get(`${basePath}/:id`, async (c) => {
|
|
3295
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3104
3296
|
const id = c.req.param("id");
|
|
3105
3297
|
const queryDict = c.req.query();
|
|
3106
3298
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3131,6 +3323,7 @@
|
|
|
3131
3323
|
});
|
|
3132
3324
|
this.router.post(basePath, async (c) => {
|
|
3133
3325
|
try {
|
|
3326
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3134
3327
|
const driver = c.get("driver") || this.driver;
|
|
3135
3328
|
const path2 = collection.slug;
|
|
3136
3329
|
const hookCtx = this.buildHookContext(c, "POST");
|
|
@@ -3159,6 +3352,7 @@
|
|
|
3159
3352
|
});
|
|
3160
3353
|
this.router.put(`${basePath}/:id`, async (c) => {
|
|
3161
3354
|
try {
|
|
3355
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3162
3356
|
const id = c.req.param("id");
|
|
3163
3357
|
const driver = c.get("driver") || this.driver;
|
|
3164
3358
|
const hookCtx = this.buildHookContext(c, "PUT");
|
|
@@ -3195,6 +3389,7 @@
|
|
|
3195
3389
|
}
|
|
3196
3390
|
});
|
|
3197
3391
|
this.router.delete(`${basePath}/:id`, async (c) => {
|
|
3392
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3198
3393
|
const id = c.req.param("id");
|
|
3199
3394
|
const driver = c.get("driver") || this.driver;
|
|
3200
3395
|
const hookCtx = this.buildHookContext(c, "DELETE");
|
|
@@ -3266,6 +3461,7 @@
|
|
|
3266
3461
|
const parsed = parseSubPath(rawPath);
|
|
3267
3462
|
if (!parsed) return next();
|
|
3268
3463
|
const driver = c.get("driver") || this.driver;
|
|
3464
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3269
3465
|
if (parsed.entityId === "count") {
|
|
3270
3466
|
const queryDict = c.req.query();
|
|
3271
3467
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3313,6 +3509,7 @@
|
|
|
3313
3509
|
const parsed = parseSubPath(rawPath);
|
|
3314
3510
|
if (!parsed || parsed.entityId) return next();
|
|
3315
3511
|
const driver = c.get("driver") || this.driver;
|
|
3512
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3316
3513
|
const body = await c.req.json().catch(() => ({}));
|
|
3317
3514
|
const entity = await driver.saveEntity({
|
|
3318
3515
|
path: parsed.collectionPath,
|
|
@@ -3328,6 +3525,7 @@
|
|
|
3328
3525
|
const parsed = parseSubPath(rawPath);
|
|
3329
3526
|
if (!parsed || !parsed.entityId) return next();
|
|
3330
3527
|
const driver = c.get("driver") || this.driver;
|
|
3528
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3331
3529
|
const body = await c.req.json().catch(() => ({}));
|
|
3332
3530
|
const entity = await driver.saveEntity({
|
|
3333
3531
|
path: parsed.collectionPath,
|
|
@@ -3344,6 +3542,7 @@
|
|
|
3344
3542
|
const parsed = parseSubPath(rawPath);
|
|
3345
3543
|
if (!parsed || !parsed.entityId) return next();
|
|
3346
3544
|
const driver = c.get("driver") || this.driver;
|
|
3545
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3347
3546
|
const existingEntity = await driver.fetchEntity({
|
|
3348
3547
|
path: parsed.collectionPath,
|
|
3349
3548
|
entityId: parsed.entityId
|
|
@@ -3409,7 +3608,8 @@
|
|
|
3409
3608
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
3410
3609
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3411
3610
|
startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
|
|
3412
|
-
searchString
|
|
3611
|
+
searchString,
|
|
3612
|
+
vectorSearch: queryOptions.vectorSearch
|
|
3413
3613
|
});
|
|
3414
3614
|
return entities.map((entity) => this.flattenEntity(entity));
|
|
3415
3615
|
}
|
|
@@ -4935,7 +5135,7 @@
|
|
|
4935
5135
|
const SemVer$6 = semver$4;
|
|
4936
5136
|
const parse$6 = parse_1;
|
|
4937
5137
|
const { safeRe: re, t } = reExports;
|
|
4938
|
-
const coerce$
|
|
5138
|
+
const coerce$2 = (version2, options2) => {
|
|
4939
5139
|
if (version2 instanceof SemVer$6) {
|
|
4940
5140
|
return version2;
|
|
4941
5141
|
}
|
|
@@ -4970,7 +5170,7 @@
|
|
|
4970
5170
|
const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
4971
5171
|
return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
|
|
4972
5172
|
};
|
|
4973
|
-
var coerce_1 = coerce$
|
|
5173
|
+
var coerce_1 = coerce$2;
|
|
4974
5174
|
const parse$5 = parse_1;
|
|
4975
5175
|
const constants$1 = constants$2;
|
|
4976
5176
|
const SemVer$5 = semver$4;
|
|
@@ -5256,19 +5456,19 @@
|
|
|
5256
5456
|
const replaceCaret = (comp, options2) => {
|
|
5257
5457
|
debug2("caret", comp, options2);
|
|
5258
5458
|
const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
|
|
5259
|
-
const
|
|
5459
|
+
const z2 = options2.includePrerelease ? "-0" : "";
|
|
5260
5460
|
return comp.replace(r, (_, M, m2, p, pr) => {
|
|
5261
5461
|
debug2("caret", comp, _, M, m2, p, pr);
|
|
5262
5462
|
let ret;
|
|
5263
5463
|
if (isX(M)) {
|
|
5264
5464
|
ret = "";
|
|
5265
5465
|
} else if (isX(m2)) {
|
|
5266
|
-
ret = `>=${M}.0.0${
|
|
5466
|
+
ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
|
|
5267
5467
|
} else if (isX(p)) {
|
|
5268
5468
|
if (M === "0") {
|
|
5269
|
-
ret = `>=${M}.${m2}.0${
|
|
5469
|
+
ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
|
|
5270
5470
|
} else {
|
|
5271
|
-
ret = `>=${M}.${m2}.0${
|
|
5471
|
+
ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
|
|
5272
5472
|
}
|
|
5273
5473
|
} else if (pr) {
|
|
5274
5474
|
debug2("replaceCaret pr", pr);
|
|
@@ -5285,9 +5485,9 @@
|
|
|
5285
5485
|
debug2("no pr");
|
|
5286
5486
|
if (M === "0") {
|
|
5287
5487
|
if (m2 === "0") {
|
|
5288
|
-
ret = `>=${M}.${m2}.${p}${
|
|
5488
|
+
ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
|
|
5289
5489
|
} else {
|
|
5290
|
-
ret = `>=${M}.${m2}.${p}${
|
|
5490
|
+
ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
|
|
5291
5491
|
}
|
|
5292
5492
|
} else {
|
|
5293
5493
|
ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
|
|
@@ -5944,7 +6144,7 @@
|
|
|
5944
6144
|
const gte = gte_1;
|
|
5945
6145
|
const lte = lte_1;
|
|
5946
6146
|
const cmp = cmp_1;
|
|
5947
|
-
const coerce = coerce_1;
|
|
6147
|
+
const coerce$1 = coerce_1;
|
|
5948
6148
|
const truncate = truncate_1;
|
|
5949
6149
|
const Comparator = requireComparator();
|
|
5950
6150
|
const Range = requireRange();
|
|
@@ -5983,7 +6183,7 @@
|
|
|
5983
6183
|
gte,
|
|
5984
6184
|
lte,
|
|
5985
6185
|
cmp,
|
|
5986
|
-
coerce,
|
|
6186
|
+
coerce: coerce$1,
|
|
5987
6187
|
truncate,
|
|
5988
6188
|
Comparator,
|
|
5989
6189
|
Range,
|
|
@@ -6864,7 +7064,7 @@
|
|
|
6864
7064
|
NotBeforeError: NotBeforeError_1,
|
|
6865
7065
|
TokenExpiredError: TokenExpiredError_1
|
|
6866
7066
|
};
|
|
6867
|
-
const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
|
|
7067
|
+
const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
|
|
6868
7068
|
let jwtConfig = {
|
|
6869
7069
|
secret: "",
|
|
6870
7070
|
accessExpiresIn: "1h",
|
|
@@ -6883,15 +7083,17 @@
|
|
|
6883
7083
|
...config
|
|
6884
7084
|
};
|
|
6885
7085
|
}
|
|
6886
|
-
function generateAccessToken(userId, roles) {
|
|
7086
|
+
function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
|
|
6887
7087
|
if (!jwtConfig.secret) {
|
|
6888
7088
|
throw new Error("JWT secret not configured. Call configureJwt() first.");
|
|
6889
7089
|
}
|
|
6890
7090
|
const payload = {
|
|
6891
7091
|
userId,
|
|
6892
|
-
roles
|
|
7092
|
+
roles,
|
|
7093
|
+
aal,
|
|
7094
|
+
...customClaims
|
|
6893
7095
|
};
|
|
6894
|
-
return jwt.sign(payload, jwtConfig.secret, {
|
|
7096
|
+
return jwt$1.sign(payload, jwtConfig.secret, {
|
|
6895
7097
|
expiresIn: jwtConfig.accessExpiresIn,
|
|
6896
7098
|
algorithm: "HS256"
|
|
6897
7099
|
});
|
|
@@ -6925,7 +7127,7 @@
|
|
|
6925
7127
|
throw new Error("JWT secret not configured. Call configureJwt() first.");
|
|
6926
7128
|
}
|
|
6927
7129
|
try {
|
|
6928
|
-
const decoded = jwt.verify(token, jwtConfig.secret, {
|
|
7130
|
+
const decoded = jwt$1.verify(token, jwtConfig.secret, {
|
|
6929
7131
|
algorithms: ["HS256"]
|
|
6930
7132
|
});
|
|
6931
7133
|
const id = decoded.userId || decoded.uid || decoded.sub;
|
|
@@ -6933,9 +7135,11 @@
|
|
|
6933
7135
|
console.error("[JWT] Verification failed: missing id in payload", decoded);
|
|
6934
7136
|
return null;
|
|
6935
7137
|
}
|
|
7138
|
+
const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
|
|
6936
7139
|
return {
|
|
6937
7140
|
userId: id,
|
|
6938
|
-
roles: decoded.roles || []
|
|
7141
|
+
roles: decoded.roles || [],
|
|
7142
|
+
aal
|
|
6939
7143
|
};
|
|
6940
7144
|
} catch (error2) {
|
|
6941
7145
|
console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
|
|
@@ -6975,6 +7179,17 @@
|
|
|
6975
7179
|
}
|
|
6976
7180
|
return new Date(Date.now() + ms2);
|
|
6977
7181
|
}
|
|
7182
|
+
const jwt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
7183
|
+
__proto__: null,
|
|
7184
|
+
configureJwt,
|
|
7185
|
+
generateAccessToken,
|
|
7186
|
+
generateRefreshToken,
|
|
7187
|
+
getAccessTokenExpiry,
|
|
7188
|
+
getAccessTokenExpiryMs,
|
|
7189
|
+
getRefreshTokenExpiry,
|
|
7190
|
+
hashRefreshToken,
|
|
7191
|
+
verifyAccessToken
|
|
7192
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
6978
7193
|
function isRLSScopedDriver(driver) {
|
|
6979
7194
|
return "withAuth" in driver && typeof driver.withAuth === "function";
|
|
6980
7195
|
}
|
|
@@ -6997,6 +7212,81 @@
|
|
|
6997
7212
|
return false;
|
|
6998
7213
|
}
|
|
6999
7214
|
}
|
|
7215
|
+
function isApiKeyToken(token) {
|
|
7216
|
+
return token.startsWith("rk_");
|
|
7217
|
+
}
|
|
7218
|
+
function hashToken$2(token) {
|
|
7219
|
+
return require$$0$5.createHash("sha256").update(token).digest("hex");
|
|
7220
|
+
}
|
|
7221
|
+
async function validateApiKey(c, token, options2) {
|
|
7222
|
+
const {
|
|
7223
|
+
store,
|
|
7224
|
+
driver
|
|
7225
|
+
} = options2;
|
|
7226
|
+
const hash = hashToken$2(token);
|
|
7227
|
+
const apiKey = await store.findByKeyHash(hash);
|
|
7228
|
+
if (!apiKey) {
|
|
7229
|
+
return c.json({
|
|
7230
|
+
error: {
|
|
7231
|
+
message: "Invalid API key",
|
|
7232
|
+
code: "UNAUTHORIZED"
|
|
7233
|
+
}
|
|
7234
|
+
}, 401);
|
|
7235
|
+
}
|
|
7236
|
+
if (apiKey.revoked_at) {
|
|
7237
|
+
return c.json({
|
|
7238
|
+
error: {
|
|
7239
|
+
message: "API key has been revoked",
|
|
7240
|
+
code: "UNAUTHORIZED"
|
|
7241
|
+
}
|
|
7242
|
+
}, 401);
|
|
7243
|
+
}
|
|
7244
|
+
if (apiKey.expires_at && new Date(apiKey.expires_at) < /* @__PURE__ */ new Date()) {
|
|
7245
|
+
return c.json({
|
|
7246
|
+
error: {
|
|
7247
|
+
message: "API key has expired",
|
|
7248
|
+
code: "UNAUTHORIZED"
|
|
7249
|
+
}
|
|
7250
|
+
}, 401);
|
|
7251
|
+
}
|
|
7252
|
+
const userId = `api-key:${apiKey.id}`;
|
|
7253
|
+
c.set("user", {
|
|
7254
|
+
userId,
|
|
7255
|
+
roles: []
|
|
7256
|
+
});
|
|
7257
|
+
const masked = {
|
|
7258
|
+
id: apiKey.id,
|
|
7259
|
+
name: apiKey.name,
|
|
7260
|
+
key_prefix: apiKey.key_prefix,
|
|
7261
|
+
permissions: apiKey.permissions,
|
|
7262
|
+
rate_limit: apiKey.rate_limit,
|
|
7263
|
+
created_by: apiKey.created_by,
|
|
7264
|
+
created_at: apiKey.created_at,
|
|
7265
|
+
updated_at: apiKey.updated_at,
|
|
7266
|
+
last_used_at: apiKey.last_used_at,
|
|
7267
|
+
expires_at: apiKey.expires_at,
|
|
7268
|
+
revoked_at: apiKey.revoked_at
|
|
7269
|
+
};
|
|
7270
|
+
c.set("apiKey", masked);
|
|
7271
|
+
try {
|
|
7272
|
+
const scopedDriver = await scopeDataDriver(driver, {
|
|
7273
|
+
uid: userId,
|
|
7274
|
+
roles: ["service"]
|
|
7275
|
+
});
|
|
7276
|
+
c.set("driver", scopedDriver);
|
|
7277
|
+
} catch (error2) {
|
|
7278
|
+
console.error("[AUTH] RLS scoping failed for API key:", error2);
|
|
7279
|
+
return c.json({
|
|
7280
|
+
error: {
|
|
7281
|
+
message: "Internal authentication error",
|
|
7282
|
+
code: "INTERNAL_ERROR"
|
|
7283
|
+
}
|
|
7284
|
+
}, 500);
|
|
7285
|
+
}
|
|
7286
|
+
store.updateLastUsed(apiKey.id).catch(() => {
|
|
7287
|
+
});
|
|
7288
|
+
return true;
|
|
7289
|
+
}
|
|
7000
7290
|
const requireAuth = async (c, next) => {
|
|
7001
7291
|
const authHeader = c.req.header("authorization");
|
|
7002
7292
|
const queryToken = c.req.query("token");
|
|
@@ -7103,7 +7393,8 @@
|
|
|
7103
7393
|
driver,
|
|
7104
7394
|
requireAuth: enforceAuth = true,
|
|
7105
7395
|
validator,
|
|
7106
|
-
serviceKey
|
|
7396
|
+
serviceKey,
|
|
7397
|
+
apiKeyStore
|
|
7107
7398
|
} = options2;
|
|
7108
7399
|
return async (c, next) => {
|
|
7109
7400
|
if (validator) {
|
|
@@ -7178,6 +7469,14 @@
|
|
|
7178
7469
|
}
|
|
7179
7470
|
}, 500);
|
|
7180
7471
|
}
|
|
7472
|
+
} else if (apiKeyStore && isApiKeyToken(token)) {
|
|
7473
|
+
const result = await validateApiKey(c, token, {
|
|
7474
|
+
store: apiKeyStore,
|
|
7475
|
+
driver
|
|
7476
|
+
});
|
|
7477
|
+
if (result !== true) {
|
|
7478
|
+
return result;
|
|
7479
|
+
}
|
|
7181
7480
|
} else {
|
|
7182
7481
|
const payload = extractUserFromToken(token);
|
|
7183
7482
|
if (payload) {
|
|
@@ -7238,9 +7537,22 @@
|
|
|
7238
7537
|
const {
|
|
7239
7538
|
adapter,
|
|
7240
7539
|
driver,
|
|
7241
|
-
requireAuth: enforceAuth = true
|
|
7540
|
+
requireAuth: enforceAuth = true,
|
|
7541
|
+
apiKeyStore
|
|
7242
7542
|
} = options2;
|
|
7243
7543
|
return async (c, next) => {
|
|
7544
|
+
if (apiKeyStore) {
|
|
7545
|
+
const authHeader = c.req.header("authorization") || "";
|
|
7546
|
+
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
7547
|
+
if (token.startsWith("rk_")) {
|
|
7548
|
+
const result = await validateApiKey(c, token, {
|
|
7549
|
+
store: apiKeyStore,
|
|
7550
|
+
driver
|
|
7551
|
+
});
|
|
7552
|
+
if (result === true) return next();
|
|
7553
|
+
return result;
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7244
7556
|
let authenticatedUser = null;
|
|
7245
7557
|
try {
|
|
7246
7558
|
authenticatedUser = await adapter.verifyRequest(c.req.raw);
|
|
@@ -7336,11 +7648,11 @@
|
|
|
7336
7648
|
const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
|
|
7337
7649
|
return require$$0$5.timingSafeEqual(derivedKey, storedKey);
|
|
7338
7650
|
}
|
|
7339
|
-
function
|
|
7651
|
+
function resolveAuthHooks(hooks) {
|
|
7340
7652
|
return {
|
|
7341
|
-
hashPassword:
|
|
7342
|
-
verifyPassword:
|
|
7343
|
-
validatePasswordStrength:
|
|
7653
|
+
hashPassword: hooks?.hashPassword ?? hashPassword,
|
|
7654
|
+
verifyPassword: hooks?.verifyPassword ?? verifyPassword,
|
|
7655
|
+
validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
|
|
7344
7656
|
};
|
|
7345
7657
|
}
|
|
7346
7658
|
function getGreeting(user) {
|
|
@@ -7742,6 +8054,63 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7742
8054
|
limit: 50,
|
|
7743
8055
|
message: "Too many requests to this sensitive endpoint, please try again later."
|
|
7744
8056
|
});
|
|
8057
|
+
function apiKeyKeyGenerator(c) {
|
|
8058
|
+
const apiKey = c.get("apiKey");
|
|
8059
|
+
if (apiKey) {
|
|
8060
|
+
return `api-key:${apiKey.id}`;
|
|
8061
|
+
}
|
|
8062
|
+
return defaultKeyGenerator(c);
|
|
8063
|
+
}
|
|
8064
|
+
function createApiKeyRateLimiter(defaultLimit = 1e3, windowMs = 15 * 60 * 1e3) {
|
|
8065
|
+
const store = /* @__PURE__ */ new Map();
|
|
8066
|
+
const cleanupInterval = setInterval(() => {
|
|
8067
|
+
const now = Date.now();
|
|
8068
|
+
for (const [key, entry] of store.entries()) {
|
|
8069
|
+
entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
|
|
8070
|
+
if (entry.timestamps.length === 0) {
|
|
8071
|
+
store.delete(key);
|
|
8072
|
+
}
|
|
8073
|
+
}
|
|
8074
|
+
}, windowMs);
|
|
8075
|
+
if (cleanupInterval.unref) {
|
|
8076
|
+
cleanupInterval.unref();
|
|
8077
|
+
}
|
|
8078
|
+
return async (c, next) => {
|
|
8079
|
+
const apiKey = c.get("apiKey");
|
|
8080
|
+
if (!apiKey) {
|
|
8081
|
+
return next();
|
|
8082
|
+
}
|
|
8083
|
+
const limit = apiKey.rate_limit ?? defaultLimit;
|
|
8084
|
+
const key = `api-key:${apiKey.id}`;
|
|
8085
|
+
const now = Date.now();
|
|
8086
|
+
let entry = store.get(key);
|
|
8087
|
+
if (!entry) {
|
|
8088
|
+
entry = {
|
|
8089
|
+
timestamps: []
|
|
8090
|
+
};
|
|
8091
|
+
store.set(key, entry);
|
|
8092
|
+
}
|
|
8093
|
+
entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
|
|
8094
|
+
if (entry.timestamps.length >= limit) {
|
|
8095
|
+
const retryAfterMs = entry.timestamps[0] + windowMs - now;
|
|
8096
|
+
const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
|
|
8097
|
+
c.header("Retry-After", String(retryAfterSec));
|
|
8098
|
+
c.header("X-RateLimit-Limit", String(limit));
|
|
8099
|
+
c.header("X-RateLimit-Remaining", "0");
|
|
8100
|
+
c.header("X-RateLimit-Reset", String(Math.ceil((now + retryAfterMs) / 1e3)));
|
|
8101
|
+
return c.json({
|
|
8102
|
+
error: {
|
|
8103
|
+
message: "API key rate limit exceeded, please try again later.",
|
|
8104
|
+
code: "RATE_LIMITED"
|
|
8105
|
+
}
|
|
8106
|
+
}, 429);
|
|
8107
|
+
}
|
|
8108
|
+
entry.timestamps.push(now);
|
|
8109
|
+
c.header("X-RateLimit-Limit", String(limit));
|
|
8110
|
+
c.header("X-RateLimit-Remaining", String(limit - entry.timestamps.length));
|
|
8111
|
+
return next();
|
|
8112
|
+
};
|
|
8113
|
+
}
|
|
7745
8114
|
var util$3;
|
|
7746
8115
|
(function(util2) {
|
|
7747
8116
|
util2.assertEqual = (_) => {
|
|
@@ -7892,6 +8261,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7892
8261
|
"not_multiple_of",
|
|
7893
8262
|
"not_finite"
|
|
7894
8263
|
]);
|
|
8264
|
+
const quotelessJson = (obj) => {
|
|
8265
|
+
const json = JSON.stringify(obj, null, 2);
|
|
8266
|
+
return json.replace(/"([^"]+)":/g, "$1:");
|
|
8267
|
+
};
|
|
7895
8268
|
class ZodError extends Error {
|
|
7896
8269
|
get errors() {
|
|
7897
8270
|
return this.issues;
|
|
@@ -8087,6 +8460,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
8087
8460
|
return { message };
|
|
8088
8461
|
};
|
|
8089
8462
|
let overrideErrorMap = errorMap;
|
|
8463
|
+
function setErrorMap(map2) {
|
|
8464
|
+
overrideErrorMap = map2;
|
|
8465
|
+
}
|
|
8090
8466
|
function getErrorMap() {
|
|
8091
8467
|
return overrideErrorMap;
|
|
8092
8468
|
}
|
|
@@ -8115,6 +8491,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
8115
8491
|
message: errorMessage
|
|
8116
8492
|
};
|
|
8117
8493
|
};
|
|
8494
|
+
const EMPTY_PATH = [];
|
|
8118
8495
|
function addIssueToContext(ctx, issueData) {
|
|
8119
8496
|
const overrideMap = getErrorMap();
|
|
8120
8497
|
const issue = makeIssue({
|
|
@@ -10405,6 +10782,113 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10405
10782
|
...processCreateParams(params)
|
|
10406
10783
|
});
|
|
10407
10784
|
};
|
|
10785
|
+
const getDiscriminator = (type) => {
|
|
10786
|
+
if (type instanceof ZodLazy) {
|
|
10787
|
+
return getDiscriminator(type.schema);
|
|
10788
|
+
} else if (type instanceof ZodEffects) {
|
|
10789
|
+
return getDiscriminator(type.innerType());
|
|
10790
|
+
} else if (type instanceof ZodLiteral) {
|
|
10791
|
+
return [type.value];
|
|
10792
|
+
} else if (type instanceof ZodEnum) {
|
|
10793
|
+
return type.options;
|
|
10794
|
+
} else if (type instanceof ZodNativeEnum) {
|
|
10795
|
+
return util$3.objectValues(type.enum);
|
|
10796
|
+
} else if (type instanceof ZodDefault) {
|
|
10797
|
+
return getDiscriminator(type._def.innerType);
|
|
10798
|
+
} else if (type instanceof ZodUndefined) {
|
|
10799
|
+
return [void 0];
|
|
10800
|
+
} else if (type instanceof ZodNull) {
|
|
10801
|
+
return [null];
|
|
10802
|
+
} else if (type instanceof ZodOptional) {
|
|
10803
|
+
return [void 0, ...getDiscriminator(type.unwrap())];
|
|
10804
|
+
} else if (type instanceof ZodNullable) {
|
|
10805
|
+
return [null, ...getDiscriminator(type.unwrap())];
|
|
10806
|
+
} else if (type instanceof ZodBranded) {
|
|
10807
|
+
return getDiscriminator(type.unwrap());
|
|
10808
|
+
} else if (type instanceof ZodReadonly) {
|
|
10809
|
+
return getDiscriminator(type.unwrap());
|
|
10810
|
+
} else if (type instanceof ZodCatch) {
|
|
10811
|
+
return getDiscriminator(type._def.innerType);
|
|
10812
|
+
} else {
|
|
10813
|
+
return [];
|
|
10814
|
+
}
|
|
10815
|
+
};
|
|
10816
|
+
class ZodDiscriminatedUnion extends ZodType {
|
|
10817
|
+
_parse(input) {
|
|
10818
|
+
const { ctx } = this._processInputParams(input);
|
|
10819
|
+
if (ctx.parsedType !== ZodParsedType.object) {
|
|
10820
|
+
addIssueToContext(ctx, {
|
|
10821
|
+
code: ZodIssueCode.invalid_type,
|
|
10822
|
+
expected: ZodParsedType.object,
|
|
10823
|
+
received: ctx.parsedType
|
|
10824
|
+
});
|
|
10825
|
+
return INVALID;
|
|
10826
|
+
}
|
|
10827
|
+
const discriminator = this.discriminator;
|
|
10828
|
+
const discriminatorValue = ctx.data[discriminator];
|
|
10829
|
+
const option = this.optionsMap.get(discriminatorValue);
|
|
10830
|
+
if (!option) {
|
|
10831
|
+
addIssueToContext(ctx, {
|
|
10832
|
+
code: ZodIssueCode.invalid_union_discriminator,
|
|
10833
|
+
options: Array.from(this.optionsMap.keys()),
|
|
10834
|
+
path: [discriminator]
|
|
10835
|
+
});
|
|
10836
|
+
return INVALID;
|
|
10837
|
+
}
|
|
10838
|
+
if (ctx.common.async) {
|
|
10839
|
+
return option._parseAsync({
|
|
10840
|
+
data: ctx.data,
|
|
10841
|
+
path: ctx.path,
|
|
10842
|
+
parent: ctx
|
|
10843
|
+
});
|
|
10844
|
+
} else {
|
|
10845
|
+
return option._parseSync({
|
|
10846
|
+
data: ctx.data,
|
|
10847
|
+
path: ctx.path,
|
|
10848
|
+
parent: ctx
|
|
10849
|
+
});
|
|
10850
|
+
}
|
|
10851
|
+
}
|
|
10852
|
+
get discriminator() {
|
|
10853
|
+
return this._def.discriminator;
|
|
10854
|
+
}
|
|
10855
|
+
get options() {
|
|
10856
|
+
return this._def.options;
|
|
10857
|
+
}
|
|
10858
|
+
get optionsMap() {
|
|
10859
|
+
return this._def.optionsMap;
|
|
10860
|
+
}
|
|
10861
|
+
/**
|
|
10862
|
+
* The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
|
|
10863
|
+
* However, it only allows a union of objects, all of which need to share a discriminator property. This property must
|
|
10864
|
+
* have a different value for each object in the union.
|
|
10865
|
+
* @param discriminator the name of the discriminator property
|
|
10866
|
+
* @param types an array of object schemas
|
|
10867
|
+
* @param params
|
|
10868
|
+
*/
|
|
10869
|
+
static create(discriminator, options2, params) {
|
|
10870
|
+
const optionsMap = /* @__PURE__ */ new Map();
|
|
10871
|
+
for (const type of options2) {
|
|
10872
|
+
const discriminatorValues = getDiscriminator(type.shape[discriminator]);
|
|
10873
|
+
if (!discriminatorValues.length) {
|
|
10874
|
+
throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
|
|
10875
|
+
}
|
|
10876
|
+
for (const value of discriminatorValues) {
|
|
10877
|
+
if (optionsMap.has(value)) {
|
|
10878
|
+
throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
|
|
10879
|
+
}
|
|
10880
|
+
optionsMap.set(value, type);
|
|
10881
|
+
}
|
|
10882
|
+
}
|
|
10883
|
+
return new ZodDiscriminatedUnion({
|
|
10884
|
+
typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
|
|
10885
|
+
discriminator,
|
|
10886
|
+
options: options2,
|
|
10887
|
+
optionsMap,
|
|
10888
|
+
...processCreateParams(params)
|
|
10889
|
+
});
|
|
10890
|
+
}
|
|
10891
|
+
}
|
|
10408
10892
|
function mergeValues(a2, b) {
|
|
10409
10893
|
const aType = getParsedType(a2);
|
|
10410
10894
|
const bType = getParsedType(b);
|
|
@@ -10563,6 +11047,59 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10563
11047
|
...processCreateParams(params)
|
|
10564
11048
|
});
|
|
10565
11049
|
};
|
|
11050
|
+
class ZodRecord extends ZodType {
|
|
11051
|
+
get keySchema() {
|
|
11052
|
+
return this._def.keyType;
|
|
11053
|
+
}
|
|
11054
|
+
get valueSchema() {
|
|
11055
|
+
return this._def.valueType;
|
|
11056
|
+
}
|
|
11057
|
+
_parse(input) {
|
|
11058
|
+
const { status, ctx } = this._processInputParams(input);
|
|
11059
|
+
if (ctx.parsedType !== ZodParsedType.object) {
|
|
11060
|
+
addIssueToContext(ctx, {
|
|
11061
|
+
code: ZodIssueCode.invalid_type,
|
|
11062
|
+
expected: ZodParsedType.object,
|
|
11063
|
+
received: ctx.parsedType
|
|
11064
|
+
});
|
|
11065
|
+
return INVALID;
|
|
11066
|
+
}
|
|
11067
|
+
const pairs = [];
|
|
11068
|
+
const keyType = this._def.keyType;
|
|
11069
|
+
const valueType = this._def.valueType;
|
|
11070
|
+
for (const key in ctx.data) {
|
|
11071
|
+
pairs.push({
|
|
11072
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
|
|
11073
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
|
|
11074
|
+
alwaysSet: key in ctx.data
|
|
11075
|
+
});
|
|
11076
|
+
}
|
|
11077
|
+
if (ctx.common.async) {
|
|
11078
|
+
return ParseStatus.mergeObjectAsync(status, pairs);
|
|
11079
|
+
} else {
|
|
11080
|
+
return ParseStatus.mergeObjectSync(status, pairs);
|
|
11081
|
+
}
|
|
11082
|
+
}
|
|
11083
|
+
get element() {
|
|
11084
|
+
return this._def.valueType;
|
|
11085
|
+
}
|
|
11086
|
+
static create(first, second, third) {
|
|
11087
|
+
if (second instanceof ZodType) {
|
|
11088
|
+
return new ZodRecord({
|
|
11089
|
+
keyType: first,
|
|
11090
|
+
valueType: second,
|
|
11091
|
+
typeName: ZodFirstPartyTypeKind.ZodRecord,
|
|
11092
|
+
...processCreateParams(third)
|
|
11093
|
+
});
|
|
11094
|
+
}
|
|
11095
|
+
return new ZodRecord({
|
|
11096
|
+
keyType: ZodString.create(),
|
|
11097
|
+
valueType: first,
|
|
11098
|
+
typeName: ZodFirstPartyTypeKind.ZodRecord,
|
|
11099
|
+
...processCreateParams(second)
|
|
11100
|
+
});
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
10566
11103
|
class ZodMap extends ZodType {
|
|
10567
11104
|
get keySchema() {
|
|
10568
11105
|
return this._def.keyType;
|
|
@@ -10714,6 +11251,111 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10714
11251
|
...processCreateParams(params)
|
|
10715
11252
|
});
|
|
10716
11253
|
};
|
|
11254
|
+
class ZodFunction extends ZodType {
|
|
11255
|
+
constructor() {
|
|
11256
|
+
super(...arguments);
|
|
11257
|
+
this.validate = this.implement;
|
|
11258
|
+
}
|
|
11259
|
+
_parse(input) {
|
|
11260
|
+
const { ctx } = this._processInputParams(input);
|
|
11261
|
+
if (ctx.parsedType !== ZodParsedType.function) {
|
|
11262
|
+
addIssueToContext(ctx, {
|
|
11263
|
+
code: ZodIssueCode.invalid_type,
|
|
11264
|
+
expected: ZodParsedType.function,
|
|
11265
|
+
received: ctx.parsedType
|
|
11266
|
+
});
|
|
11267
|
+
return INVALID;
|
|
11268
|
+
}
|
|
11269
|
+
function makeArgsIssue(args, error2) {
|
|
11270
|
+
return makeIssue({
|
|
11271
|
+
data: args,
|
|
11272
|
+
path: ctx.path,
|
|
11273
|
+
errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
|
|
11274
|
+
issueData: {
|
|
11275
|
+
code: ZodIssueCode.invalid_arguments,
|
|
11276
|
+
argumentsError: error2
|
|
11277
|
+
}
|
|
11278
|
+
});
|
|
11279
|
+
}
|
|
11280
|
+
function makeReturnsIssue(returns, error2) {
|
|
11281
|
+
return makeIssue({
|
|
11282
|
+
data: returns,
|
|
11283
|
+
path: ctx.path,
|
|
11284
|
+
errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
|
|
11285
|
+
issueData: {
|
|
11286
|
+
code: ZodIssueCode.invalid_return_type,
|
|
11287
|
+
returnTypeError: error2
|
|
11288
|
+
}
|
|
11289
|
+
});
|
|
11290
|
+
}
|
|
11291
|
+
const params = { errorMap: ctx.common.contextualErrorMap };
|
|
11292
|
+
const fn = ctx.data;
|
|
11293
|
+
if (this._def.returns instanceof ZodPromise) {
|
|
11294
|
+
const me = this;
|
|
11295
|
+
return OK(async function(...args) {
|
|
11296
|
+
const error2 = new ZodError([]);
|
|
11297
|
+
const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
|
|
11298
|
+
error2.addIssue(makeArgsIssue(args, e));
|
|
11299
|
+
throw error2;
|
|
11300
|
+
});
|
|
11301
|
+
const result = await Reflect.apply(fn, this, parsedArgs);
|
|
11302
|
+
const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
|
|
11303
|
+
error2.addIssue(makeReturnsIssue(result, e));
|
|
11304
|
+
throw error2;
|
|
11305
|
+
});
|
|
11306
|
+
return parsedReturns;
|
|
11307
|
+
});
|
|
11308
|
+
} else {
|
|
11309
|
+
const me = this;
|
|
11310
|
+
return OK(function(...args) {
|
|
11311
|
+
const parsedArgs = me._def.args.safeParse(args, params);
|
|
11312
|
+
if (!parsedArgs.success) {
|
|
11313
|
+
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
|
|
11314
|
+
}
|
|
11315
|
+
const result = Reflect.apply(fn, this, parsedArgs.data);
|
|
11316
|
+
const parsedReturns = me._def.returns.safeParse(result, params);
|
|
11317
|
+
if (!parsedReturns.success) {
|
|
11318
|
+
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
|
|
11319
|
+
}
|
|
11320
|
+
return parsedReturns.data;
|
|
11321
|
+
});
|
|
11322
|
+
}
|
|
11323
|
+
}
|
|
11324
|
+
parameters() {
|
|
11325
|
+
return this._def.args;
|
|
11326
|
+
}
|
|
11327
|
+
returnType() {
|
|
11328
|
+
return this._def.returns;
|
|
11329
|
+
}
|
|
11330
|
+
args(...items) {
|
|
11331
|
+
return new ZodFunction({
|
|
11332
|
+
...this._def,
|
|
11333
|
+
args: ZodTuple.create(items).rest(ZodUnknown.create())
|
|
11334
|
+
});
|
|
11335
|
+
}
|
|
11336
|
+
returns(returnType) {
|
|
11337
|
+
return new ZodFunction({
|
|
11338
|
+
...this._def,
|
|
11339
|
+
returns: returnType
|
|
11340
|
+
});
|
|
11341
|
+
}
|
|
11342
|
+
implement(func) {
|
|
11343
|
+
const validatedFunc = this.parse(func);
|
|
11344
|
+
return validatedFunc;
|
|
11345
|
+
}
|
|
11346
|
+
strictImplement(func) {
|
|
11347
|
+
const validatedFunc = this.parse(func);
|
|
11348
|
+
return validatedFunc;
|
|
11349
|
+
}
|
|
11350
|
+
static create(args, returns, params) {
|
|
11351
|
+
return new ZodFunction({
|
|
11352
|
+
args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
|
|
11353
|
+
returns: returns || ZodUnknown.create(),
|
|
11354
|
+
typeName: ZodFirstPartyTypeKind.ZodFunction,
|
|
11355
|
+
...processCreateParams(params)
|
|
11356
|
+
});
|
|
11357
|
+
}
|
|
11358
|
+
}
|
|
10717
11359
|
class ZodLazy extends ZodType {
|
|
10718
11360
|
get schema() {
|
|
10719
11361
|
return this._def.getter();
|
|
@@ -11171,6 +11813,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11171
11813
|
...processCreateParams(params)
|
|
11172
11814
|
});
|
|
11173
11815
|
};
|
|
11816
|
+
const BRAND = Symbol("zod_brand");
|
|
11174
11817
|
class ZodBranded extends ZodType {
|
|
11175
11818
|
_parse(input) {
|
|
11176
11819
|
const { ctx } = this._processInputParams(input);
|
|
@@ -11262,6 +11905,36 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11262
11905
|
...processCreateParams(params)
|
|
11263
11906
|
});
|
|
11264
11907
|
};
|
|
11908
|
+
function cleanParams(params, data) {
|
|
11909
|
+
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
11910
|
+
const p2 = typeof p === "string" ? { message: p } : p;
|
|
11911
|
+
return p2;
|
|
11912
|
+
}
|
|
11913
|
+
function custom(check, _params = {}, fatal) {
|
|
11914
|
+
if (check)
|
|
11915
|
+
return ZodAny.create().superRefine((data, ctx) => {
|
|
11916
|
+
const r = check(data);
|
|
11917
|
+
if (r instanceof Promise) {
|
|
11918
|
+
return r.then((r2) => {
|
|
11919
|
+
if (!r2) {
|
|
11920
|
+
const params = cleanParams(_params, data);
|
|
11921
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
11922
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
11923
|
+
}
|
|
11924
|
+
});
|
|
11925
|
+
}
|
|
11926
|
+
if (!r) {
|
|
11927
|
+
const params = cleanParams(_params, data);
|
|
11928
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
11929
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
11930
|
+
}
|
|
11931
|
+
return;
|
|
11932
|
+
});
|
|
11933
|
+
return ZodAny.create();
|
|
11934
|
+
}
|
|
11935
|
+
const late = {
|
|
11936
|
+
object: ZodObject.lazycreate
|
|
11937
|
+
};
|
|
11265
11938
|
var ZodFirstPartyTypeKind;
|
|
11266
11939
|
(function(ZodFirstPartyTypeKind2) {
|
|
11267
11940
|
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
@@ -11301,17 +11974,251 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11301
11974
|
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
11302
11975
|
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
11303
11976
|
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
11977
|
+
const instanceOfType = (cls, params = {
|
|
11978
|
+
message: `Input not instance of ${cls.name}`
|
|
11979
|
+
}) => custom((data) => data instanceof cls, params);
|
|
11304
11980
|
const stringType = ZodString.create;
|
|
11305
|
-
|
|
11306
|
-
|
|
11981
|
+
const numberType = ZodNumber.create;
|
|
11982
|
+
const nanType = ZodNaN.create;
|
|
11983
|
+
const bigIntType = ZodBigInt.create;
|
|
11984
|
+
const booleanType = ZodBoolean.create;
|
|
11985
|
+
const dateType = ZodDate.create;
|
|
11986
|
+
const symbolType = ZodSymbol.create;
|
|
11987
|
+
const undefinedType = ZodUndefined.create;
|
|
11988
|
+
const nullType = ZodNull.create;
|
|
11989
|
+
const anyType = ZodAny.create;
|
|
11990
|
+
const unknownType = ZodUnknown.create;
|
|
11991
|
+
const neverType = ZodNever.create;
|
|
11992
|
+
const voidType = ZodVoid.create;
|
|
11993
|
+
const arrayType = ZodArray.create;
|
|
11307
11994
|
const objectType = ZodObject.create;
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11995
|
+
const strictObjectType = ZodObject.strictCreate;
|
|
11996
|
+
const unionType = ZodUnion.create;
|
|
11997
|
+
const discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
11998
|
+
const intersectionType = ZodIntersection.create;
|
|
11999
|
+
const tupleType = ZodTuple.create;
|
|
12000
|
+
const recordType = ZodRecord.create;
|
|
12001
|
+
const mapType = ZodMap.create;
|
|
12002
|
+
const setType = ZodSet.create;
|
|
12003
|
+
const functionType = ZodFunction.create;
|
|
12004
|
+
const lazyType = ZodLazy.create;
|
|
12005
|
+
const literalType = ZodLiteral.create;
|
|
11311
12006
|
const enumType = ZodEnum.create;
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
12007
|
+
const nativeEnumType = ZodNativeEnum.create;
|
|
12008
|
+
const promiseType = ZodPromise.create;
|
|
12009
|
+
const effectsType = ZodEffects.create;
|
|
12010
|
+
const optionalType = ZodOptional.create;
|
|
12011
|
+
const nullableType = ZodNullable.create;
|
|
12012
|
+
const preprocessType = ZodEffects.createWithPreprocess;
|
|
12013
|
+
const pipelineType = ZodPipeline.create;
|
|
12014
|
+
const ostring = () => stringType().optional();
|
|
12015
|
+
const onumber = () => numberType().optional();
|
|
12016
|
+
const oboolean = () => booleanType().optional();
|
|
12017
|
+
const coerce = {
|
|
12018
|
+
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
|
12019
|
+
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
|
12020
|
+
boolean: (arg) => ZodBoolean.create({
|
|
12021
|
+
...arg,
|
|
12022
|
+
coerce: true
|
|
12023
|
+
}),
|
|
12024
|
+
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
|
12025
|
+
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
12026
|
+
};
|
|
12027
|
+
const NEVER = INVALID;
|
|
12028
|
+
const z = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
12029
|
+
__proto__: null,
|
|
12030
|
+
BRAND,
|
|
12031
|
+
DIRTY,
|
|
12032
|
+
EMPTY_PATH,
|
|
12033
|
+
INVALID,
|
|
12034
|
+
NEVER,
|
|
12035
|
+
OK,
|
|
12036
|
+
ParseStatus,
|
|
12037
|
+
Schema: ZodType,
|
|
12038
|
+
ZodAny,
|
|
12039
|
+
ZodArray,
|
|
12040
|
+
ZodBigInt,
|
|
12041
|
+
ZodBoolean,
|
|
12042
|
+
ZodBranded,
|
|
12043
|
+
ZodCatch,
|
|
12044
|
+
ZodDate,
|
|
12045
|
+
ZodDefault,
|
|
12046
|
+
ZodDiscriminatedUnion,
|
|
12047
|
+
ZodEffects,
|
|
12048
|
+
ZodEnum,
|
|
12049
|
+
ZodError,
|
|
12050
|
+
get ZodFirstPartyTypeKind() {
|
|
12051
|
+
return ZodFirstPartyTypeKind;
|
|
12052
|
+
},
|
|
12053
|
+
ZodFunction,
|
|
12054
|
+
ZodIntersection,
|
|
12055
|
+
ZodIssueCode,
|
|
12056
|
+
ZodLazy,
|
|
12057
|
+
ZodLiteral,
|
|
12058
|
+
ZodMap,
|
|
12059
|
+
ZodNaN,
|
|
12060
|
+
ZodNativeEnum,
|
|
12061
|
+
ZodNever,
|
|
12062
|
+
ZodNull,
|
|
12063
|
+
ZodNullable,
|
|
12064
|
+
ZodNumber,
|
|
12065
|
+
ZodObject,
|
|
12066
|
+
ZodOptional,
|
|
12067
|
+
ZodParsedType,
|
|
12068
|
+
ZodPipeline,
|
|
12069
|
+
ZodPromise,
|
|
12070
|
+
ZodReadonly,
|
|
12071
|
+
ZodRecord,
|
|
12072
|
+
ZodSchema: ZodType,
|
|
12073
|
+
ZodSet,
|
|
12074
|
+
ZodString,
|
|
12075
|
+
ZodSymbol,
|
|
12076
|
+
ZodTransformer: ZodEffects,
|
|
12077
|
+
ZodTuple,
|
|
12078
|
+
ZodType,
|
|
12079
|
+
ZodUndefined,
|
|
12080
|
+
ZodUnion,
|
|
12081
|
+
ZodUnknown,
|
|
12082
|
+
ZodVoid,
|
|
12083
|
+
addIssueToContext,
|
|
12084
|
+
any: anyType,
|
|
12085
|
+
array: arrayType,
|
|
12086
|
+
bigint: bigIntType,
|
|
12087
|
+
boolean: booleanType,
|
|
12088
|
+
coerce,
|
|
12089
|
+
custom,
|
|
12090
|
+
date: dateType,
|
|
12091
|
+
datetimeRegex,
|
|
12092
|
+
defaultErrorMap: errorMap,
|
|
12093
|
+
discriminatedUnion: discriminatedUnionType,
|
|
12094
|
+
effect: effectsType,
|
|
12095
|
+
enum: enumType,
|
|
12096
|
+
function: functionType,
|
|
12097
|
+
getErrorMap,
|
|
12098
|
+
getParsedType,
|
|
12099
|
+
instanceof: instanceOfType,
|
|
12100
|
+
intersection: intersectionType,
|
|
12101
|
+
isAborted,
|
|
12102
|
+
isAsync,
|
|
12103
|
+
isDirty,
|
|
12104
|
+
isValid,
|
|
12105
|
+
late,
|
|
12106
|
+
lazy: lazyType,
|
|
12107
|
+
literal: literalType,
|
|
12108
|
+
makeIssue,
|
|
12109
|
+
map: mapType,
|
|
12110
|
+
nan: nanType,
|
|
12111
|
+
nativeEnum: nativeEnumType,
|
|
12112
|
+
never: neverType,
|
|
12113
|
+
null: nullType,
|
|
12114
|
+
nullable: nullableType,
|
|
12115
|
+
number: numberType,
|
|
12116
|
+
object: objectType,
|
|
12117
|
+
get objectUtil() {
|
|
12118
|
+
return objectUtil;
|
|
12119
|
+
},
|
|
12120
|
+
oboolean,
|
|
12121
|
+
onumber,
|
|
12122
|
+
optional: optionalType,
|
|
12123
|
+
ostring,
|
|
12124
|
+
pipeline: pipelineType,
|
|
12125
|
+
preprocess: preprocessType,
|
|
12126
|
+
promise: promiseType,
|
|
12127
|
+
quotelessJson,
|
|
12128
|
+
record: recordType,
|
|
12129
|
+
set: setType,
|
|
12130
|
+
setErrorMap,
|
|
12131
|
+
strictObject: strictObjectType,
|
|
12132
|
+
string: stringType,
|
|
12133
|
+
symbol: symbolType,
|
|
12134
|
+
transformer: effectsType,
|
|
12135
|
+
tuple: tupleType,
|
|
12136
|
+
undefined: undefinedType,
|
|
12137
|
+
union: unionType,
|
|
12138
|
+
unknown: unknownType,
|
|
12139
|
+
get util() {
|
|
12140
|
+
return util$3;
|
|
12141
|
+
},
|
|
12142
|
+
void: voidType
|
|
12143
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
12144
|
+
const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
12145
|
+
function base32Encode(buffer) {
|
|
12146
|
+
let bits = 0;
|
|
12147
|
+
let value = 0;
|
|
12148
|
+
let output = "";
|
|
12149
|
+
for (let i2 = 0; i2 < buffer.length; i2++) {
|
|
12150
|
+
value = value << 8 | buffer[i2];
|
|
12151
|
+
bits += 8;
|
|
12152
|
+
while (bits >= 5) {
|
|
12153
|
+
output += BASE32_ALPHABET[value >>> bits - 5 & 31];
|
|
12154
|
+
bits -= 5;
|
|
12155
|
+
}
|
|
12156
|
+
}
|
|
12157
|
+
if (bits > 0) {
|
|
12158
|
+
output += BASE32_ALPHABET[value << 5 - bits & 31];
|
|
12159
|
+
}
|
|
12160
|
+
return output;
|
|
12161
|
+
}
|
|
12162
|
+
function base32Decode(encoded) {
|
|
12163
|
+
const cleanInput = encoded.replace(/=+$/, "").toUpperCase();
|
|
12164
|
+
const bytes = [];
|
|
12165
|
+
let bits = 0;
|
|
12166
|
+
let value = 0;
|
|
12167
|
+
for (let i2 = 0; i2 < cleanInput.length; i2++) {
|
|
12168
|
+
const index2 = BASE32_ALPHABET.indexOf(cleanInput[i2]);
|
|
12169
|
+
if (index2 === -1) continue;
|
|
12170
|
+
value = value << 5 | index2;
|
|
12171
|
+
bits += 5;
|
|
12172
|
+
if (bits >= 8) {
|
|
12173
|
+
bytes.push(value >>> bits - 8 & 255);
|
|
12174
|
+
bits -= 8;
|
|
12175
|
+
}
|
|
12176
|
+
}
|
|
12177
|
+
return Buffer.from(bytes);
|
|
12178
|
+
}
|
|
12179
|
+
function generateHotp(secret, counter) {
|
|
12180
|
+
const hmac = require$$0$5.createHmac("sha1", secret);
|
|
12181
|
+
const counterBuffer = Buffer.alloc(8);
|
|
12182
|
+
counterBuffer.writeBigInt64BE(counter);
|
|
12183
|
+
hmac.update(counterBuffer);
|
|
12184
|
+
const hash = hmac.digest();
|
|
12185
|
+
const offset = hash[hash.length - 1] & 15;
|
|
12186
|
+
const code2 = ((hash[offset] & 127) << 24 | hash[offset + 1] << 16 | hash[offset + 2] << 8 | hash[offset + 3]) % 1e6;
|
|
12187
|
+
return code2.toString().padStart(6, "0");
|
|
12188
|
+
}
|
|
12189
|
+
function verifyTotp(secret, token, window2 = 1) {
|
|
12190
|
+
const timeStep = 30;
|
|
12191
|
+
const counter = BigInt(Math.floor(Date.now() / 1e3 / timeStep));
|
|
12192
|
+
for (let i2 = -window2; i2 <= window2; i2++) {
|
|
12193
|
+
if (generateHotp(secret, counter + BigInt(i2)) === token) {
|
|
12194
|
+
return true;
|
|
12195
|
+
}
|
|
12196
|
+
}
|
|
12197
|
+
return false;
|
|
12198
|
+
}
|
|
12199
|
+
function generateTotpSecret(issuer, accountName) {
|
|
12200
|
+
const secretBuffer = require$$0$5.randomBytes(20);
|
|
12201
|
+
const secret = base32Encode(secretBuffer);
|
|
12202
|
+
const encodedIssuer = encodeURIComponent(issuer);
|
|
12203
|
+
const encodedAccount = encodeURIComponent(accountName);
|
|
12204
|
+
const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;
|
|
12205
|
+
return {
|
|
12206
|
+
secret,
|
|
12207
|
+
uri
|
|
12208
|
+
};
|
|
12209
|
+
}
|
|
12210
|
+
function generateRecoveryCodes(count = 10) {
|
|
12211
|
+
return Array.from({
|
|
12212
|
+
length: count
|
|
12213
|
+
}, () => {
|
|
12214
|
+
const raw = require$$0$5.randomBytes(5).toString("hex").toUpperCase();
|
|
12215
|
+
const parts = raw.match(/.{1,5}/g);
|
|
12216
|
+
return parts ? parts.join("-") : raw;
|
|
12217
|
+
});
|
|
12218
|
+
}
|
|
12219
|
+
function hashRecoveryCode(code2) {
|
|
12220
|
+
return require$$0$5.createHash("sha256").update(code2.replace(/-/g, "").toUpperCase()).digest("hex");
|
|
12221
|
+
}
|
|
11315
12222
|
function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
|
|
11316
12223
|
return {
|
|
11317
12224
|
user: {
|
|
@@ -11349,9 +12256,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11349
12256
|
emailService,
|
|
11350
12257
|
emailConfig,
|
|
11351
12258
|
allowRegistration = false,
|
|
11352
|
-
|
|
12259
|
+
authHooks
|
|
11353
12260
|
} = config;
|
|
11354
|
-
const ops =
|
|
12261
|
+
const ops = resolveAuthHooks(authHooks);
|
|
11355
12262
|
const registerSchema = objectType({
|
|
11356
12263
|
email: stringType().email("Invalid email address").max(255),
|
|
11357
12264
|
password: stringType().min(1, "Password is required").max(128),
|
|
@@ -11415,7 +12322,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11415
12322
|
async function createSessionAndTokens(userId, userAgent, ipAddress) {
|
|
11416
12323
|
const roles = await authRepo.getUserRoles(userId);
|
|
11417
12324
|
const roleIds = roles.map((r) => r.id);
|
|
11418
|
-
|
|
12325
|
+
let customClaims;
|
|
12326
|
+
if (authHooks?.customizeAccessToken) {
|
|
12327
|
+
const user = await authRepo.getUserById(userId);
|
|
12328
|
+
if (user) {
|
|
12329
|
+
const defaultClaims = {
|
|
12330
|
+
userId,
|
|
12331
|
+
roles: roleIds,
|
|
12332
|
+
aal: "aal1"
|
|
12333
|
+
};
|
|
12334
|
+
customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12337
|
+
const accessToken = generateAccessToken(userId, roleIds, "aal1", customClaims);
|
|
11419
12338
|
const refreshToken = generateRefreshToken();
|
|
11420
12339
|
await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
|
|
11421
12340
|
return {
|
|
@@ -11450,8 +12369,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11450
12369
|
passwordHash,
|
|
11451
12370
|
displayName: displayName || void 0
|
|
11452
12371
|
};
|
|
11453
|
-
if (
|
|
11454
|
-
createData = await
|
|
12372
|
+
if (authHooks?.beforeUserCreate) {
|
|
12373
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
11455
12374
|
}
|
|
11456
12375
|
const user = await authRepo.createUser(createData);
|
|
11457
12376
|
const existingUsers = await authRepo.listUsers();
|
|
@@ -11470,14 +12389,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11470
12389
|
email: user.email,
|
|
11471
12390
|
displayName: user.displayName
|
|
11472
12391
|
});
|
|
11473
|
-
if (
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
})
|
|
12392
|
+
if (authHooks?.afterUserCreate) {
|
|
12393
|
+
try {
|
|
12394
|
+
await authHooks.afterUserCreate(user);
|
|
12395
|
+
} catch (err) {
|
|
12396
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
12397
|
+
}
|
|
11477
12398
|
}
|
|
11478
|
-
if (
|
|
11479
|
-
|
|
11480
|
-
console.error("[
|
|
12399
|
+
if (authHooks?.onAuthenticated) {
|
|
12400
|
+
authHooks.onAuthenticated(user, "register").catch((err) => {
|
|
12401
|
+
console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
|
|
11481
12402
|
});
|
|
11482
12403
|
}
|
|
11483
12404
|
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
|
|
@@ -11487,9 +12408,12 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11487
12408
|
email,
|
|
11488
12409
|
password
|
|
11489
12410
|
} = parseBody2(loginSchema, await c.req.json());
|
|
12411
|
+
if (authHooks?.beforeLogin) {
|
|
12412
|
+
await authHooks.beforeLogin(email, "login");
|
|
12413
|
+
}
|
|
11490
12414
|
let user;
|
|
11491
|
-
if (
|
|
11492
|
-
user = await
|
|
12415
|
+
if (authHooks?.verifyCredentials) {
|
|
12416
|
+
user = await authHooks.verifyCredentials(email, password, authRepo);
|
|
11493
12417
|
if (!user) {
|
|
11494
12418
|
throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
|
|
11495
12419
|
}
|
|
@@ -11511,9 +12435,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11511
12435
|
accessToken,
|
|
11512
12436
|
refreshToken
|
|
11513
12437
|
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
11514
|
-
if (
|
|
11515
|
-
|
|
11516
|
-
console.error("[
|
|
12438
|
+
if (authHooks?.onAuthenticated) {
|
|
12439
|
+
authHooks.onAuthenticated(user, "login").catch((err) => {
|
|
12440
|
+
console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
|
|
11517
12441
|
});
|
|
11518
12442
|
}
|
|
11519
12443
|
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
|
|
@@ -11552,6 +12476,13 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11552
12476
|
await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
|
|
11553
12477
|
email: externalUser.email
|
|
11554
12478
|
});
|
|
12479
|
+
if (authHooks?.afterUserCreate) {
|
|
12480
|
+
try {
|
|
12481
|
+
await authHooks.afterUserCreate(user);
|
|
12482
|
+
} catch (err) {
|
|
12483
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
12484
|
+
}
|
|
12485
|
+
}
|
|
11555
12486
|
const allUsers = await authRepo.listUsers();
|
|
11556
12487
|
const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
|
|
11557
12488
|
if (isFirstUser) {
|
|
@@ -11637,6 +12568,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11637
12568
|
await authRepo.updatePassword(storedToken.userId, passwordHash);
|
|
11638
12569
|
await authRepo.markPasswordResetTokenUsed(tokenHash);
|
|
11639
12570
|
await authRepo.deleteAllRefreshTokensForUser(storedToken.userId);
|
|
12571
|
+
if (authHooks?.onPasswordReset) {
|
|
12572
|
+
authHooks.onPasswordReset(storedToken.userId).catch((err) => {
|
|
12573
|
+
console.error("[AuthHooks] onPasswordReset error:", err instanceof Error ? err.message : err);
|
|
12574
|
+
});
|
|
12575
|
+
}
|
|
11640
12576
|
return c.json({
|
|
11641
12577
|
success: true,
|
|
11642
12578
|
message: "Password has been reset successfully"
|
|
@@ -11740,7 +12676,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11740
12676
|
}
|
|
11741
12677
|
const roles = await authRepo.getUserRoles(storedToken.userId);
|
|
11742
12678
|
const roleIds = roles.map((r) => r.id);
|
|
11743
|
-
|
|
12679
|
+
let customClaims;
|
|
12680
|
+
if (authHooks?.customizeAccessToken) {
|
|
12681
|
+
const user = await authRepo.getUserById(storedToken.userId);
|
|
12682
|
+
if (user) {
|
|
12683
|
+
const defaultClaims = {
|
|
12684
|
+
userId: storedToken.userId,
|
|
12685
|
+
roles: roleIds,
|
|
12686
|
+
aal: "aal1"
|
|
12687
|
+
};
|
|
12688
|
+
customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
|
|
12689
|
+
}
|
|
12690
|
+
}
|
|
12691
|
+
const newAccessToken = generateAccessToken(storedToken.userId, roleIds, "aal1", customClaims);
|
|
11744
12692
|
const newRefreshToken = generateRefreshToken();
|
|
11745
12693
|
const userAgent = c.req.header("user-agent") || "unknown";
|
|
11746
12694
|
const ipAddress = c.req.header("x-forwarded-for") || "unknown";
|
|
@@ -11762,6 +12710,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11762
12710
|
const tokenHash = hashRefreshToken(refreshToken);
|
|
11763
12711
|
await authRepo.deleteRefreshToken(tokenHash);
|
|
11764
12712
|
}
|
|
12713
|
+
const authHeader = c.req.header("authorization");
|
|
12714
|
+
if (authHooks?.afterLogout && authHeader?.startsWith("Bearer ")) {
|
|
12715
|
+
const {
|
|
12716
|
+
verifyAccessToken: verifyAccessToken2
|
|
12717
|
+
} = await Promise.resolve().then(() => jwt);
|
|
12718
|
+
const payload = verifyAccessToken2(authHeader.substring(7));
|
|
12719
|
+
if (payload) {
|
|
12720
|
+
authHooks.afterLogout(payload.userId).catch((err) => {
|
|
12721
|
+
console.error("[AuthHooks] afterLogout error:", err instanceof Error ? err.message : err);
|
|
12722
|
+
});
|
|
12723
|
+
}
|
|
12724
|
+
}
|
|
11765
12725
|
return c.json({
|
|
11766
12726
|
success: true
|
|
11767
12727
|
});
|
|
@@ -11881,6 +12841,270 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11881
12841
|
enabledProviders
|
|
11882
12842
|
});
|
|
11883
12843
|
});
|
|
12844
|
+
router.post("/anonymous", strictAuthLimiter, async (c) => {
|
|
12845
|
+
const anonId = require$$0$5.randomBytes(16).toString("hex");
|
|
12846
|
+
const anonEmail = `anon_${anonId.slice(0, 8)}@anonymous.local`;
|
|
12847
|
+
let createData = {
|
|
12848
|
+
email: anonEmail,
|
|
12849
|
+
emailVerified: false,
|
|
12850
|
+
isAnonymous: true
|
|
12851
|
+
};
|
|
12852
|
+
if (authHooks?.beforeUserCreate) {
|
|
12853
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
12854
|
+
}
|
|
12855
|
+
const user = await authRepo.createUser(createData);
|
|
12856
|
+
if (config.defaultRole) {
|
|
12857
|
+
await authRepo.assignDefaultRole(user.id, config.defaultRole);
|
|
12858
|
+
}
|
|
12859
|
+
const {
|
|
12860
|
+
roleIds,
|
|
12861
|
+
accessToken,
|
|
12862
|
+
refreshToken
|
|
12863
|
+
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
12864
|
+
if (authHooks?.afterUserCreate) {
|
|
12865
|
+
authHooks.afterUserCreate(user).catch((err) => {
|
|
12866
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
12867
|
+
});
|
|
12868
|
+
}
|
|
12869
|
+
if (authHooks?.onAuthenticated) {
|
|
12870
|
+
authHooks.onAuthenticated(user, "anonymous").catch((err) => {
|
|
12871
|
+
console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
|
|
12872
|
+
});
|
|
12873
|
+
}
|
|
12874
|
+
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
|
|
12875
|
+
});
|
|
12876
|
+
router.post("/anonymous/link", requireAuth, async (c) => {
|
|
12877
|
+
const userCtx = c.get("user");
|
|
12878
|
+
if (!userCtx) {
|
|
12879
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12880
|
+
}
|
|
12881
|
+
const user = await authRepo.getUserById(userCtx.userId);
|
|
12882
|
+
if (!user?.isAnonymous) {
|
|
12883
|
+
throw ApiError.badRequest("User is not anonymous", "NOT_ANONYMOUS");
|
|
12884
|
+
}
|
|
12885
|
+
const linkSchema = objectType({
|
|
12886
|
+
email: stringType().email("Invalid email address").max(255),
|
|
12887
|
+
password: stringType().min(1, "Password is required").max(128)
|
|
12888
|
+
});
|
|
12889
|
+
const {
|
|
12890
|
+
email,
|
|
12891
|
+
password
|
|
12892
|
+
} = parseBody2(linkSchema, await c.req.json());
|
|
12893
|
+
const passwordValidation = ops.validatePasswordStrength(password);
|
|
12894
|
+
if (!passwordValidation.valid) {
|
|
12895
|
+
throw ApiError.badRequest(passwordValidation.errors.join(". "), "WEAK_PASSWORD");
|
|
12896
|
+
}
|
|
12897
|
+
const existingUser = await authRepo.getUserByEmail(email.toLowerCase());
|
|
12898
|
+
if (existingUser) {
|
|
12899
|
+
throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
|
|
12900
|
+
}
|
|
12901
|
+
const passwordHash = await ops.hashPassword(password);
|
|
12902
|
+
const updatedUser = await authRepo.updateUser(user.id, {
|
|
12903
|
+
email: email.toLowerCase(),
|
|
12904
|
+
passwordHash,
|
|
12905
|
+
isAnonymous: false
|
|
12906
|
+
});
|
|
12907
|
+
if (!updatedUser) {
|
|
12908
|
+
throw ApiError.notFound("User not found");
|
|
12909
|
+
}
|
|
12910
|
+
const {
|
|
12911
|
+
roleIds,
|
|
12912
|
+
accessToken,
|
|
12913
|
+
refreshToken
|
|
12914
|
+
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
12915
|
+
return c.json(buildAuthResponse(updatedUser, roleIds, accessToken, refreshToken));
|
|
12916
|
+
});
|
|
12917
|
+
router.post("/mfa/enroll", requireAuth, async (c) => {
|
|
12918
|
+
const userCtx = c.get("user");
|
|
12919
|
+
if (!userCtx) {
|
|
12920
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12921
|
+
}
|
|
12922
|
+
const body = await c.req.json().catch(() => ({}));
|
|
12923
|
+
const friendlyName = typeof body.friendlyName === "string" ? body.friendlyName : void 0;
|
|
12924
|
+
const issuer = typeof body.issuer === "string" ? body.issuer : emailConfig?.appName || "Rebase";
|
|
12925
|
+
const user = await authRepo.getUserById(userCtx.userId);
|
|
12926
|
+
if (!user) {
|
|
12927
|
+
throw ApiError.notFound("User not found");
|
|
12928
|
+
}
|
|
12929
|
+
const {
|
|
12930
|
+
secret,
|
|
12931
|
+
uri
|
|
12932
|
+
} = generateTotpSecret(issuer, user.email);
|
|
12933
|
+
const factor = await authRepo.createMfaFactor(
|
|
12934
|
+
user.id,
|
|
12935
|
+
"totp",
|
|
12936
|
+
secret,
|
|
12937
|
+
// In production, encrypt this before storage
|
|
12938
|
+
friendlyName
|
|
12939
|
+
);
|
|
12940
|
+
const codes = generateRecoveryCodes(10);
|
|
12941
|
+
const codeHashes = codes.map(hashRecoveryCode);
|
|
12942
|
+
await authRepo.createRecoveryCodes(user.id, codeHashes);
|
|
12943
|
+
return c.json({
|
|
12944
|
+
factor: {
|
|
12945
|
+
id: factor.id,
|
|
12946
|
+
factorType: factor.factorType,
|
|
12947
|
+
friendlyName: factor.friendlyName
|
|
12948
|
+
},
|
|
12949
|
+
totp: {
|
|
12950
|
+
secret,
|
|
12951
|
+
uri,
|
|
12952
|
+
qrUri: uri
|
|
12953
|
+
// Client can use a QR library to render this
|
|
12954
|
+
},
|
|
12955
|
+
recoveryCodes: codes
|
|
12956
|
+
}, 201);
|
|
12957
|
+
});
|
|
12958
|
+
router.post("/mfa/verify", requireAuth, async (c) => {
|
|
12959
|
+
const userCtx = c.get("user");
|
|
12960
|
+
if (!userCtx) {
|
|
12961
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12962
|
+
}
|
|
12963
|
+
const verifySchema = objectType({
|
|
12964
|
+
factorId: stringType().min(1, "Factor ID is required"),
|
|
12965
|
+
code: stringType().length(6, "Code must be 6 digits")
|
|
12966
|
+
});
|
|
12967
|
+
const {
|
|
12968
|
+
factorId,
|
|
12969
|
+
code: code2
|
|
12970
|
+
} = parseBody2(verifySchema, await c.req.json());
|
|
12971
|
+
const factor = await authRepo.getMfaFactorById(factorId);
|
|
12972
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
12973
|
+
throw ApiError.notFound("MFA factor not found");
|
|
12974
|
+
}
|
|
12975
|
+
if (factor.verified) {
|
|
12976
|
+
throw ApiError.badRequest("Factor is already verified", "ALREADY_VERIFIED");
|
|
12977
|
+
}
|
|
12978
|
+
const secretBuffer = base32Decode(factor.secretEncrypted);
|
|
12979
|
+
const isValid2 = verifyTotp(secretBuffer, code2);
|
|
12980
|
+
if (!isValid2) {
|
|
12981
|
+
throw ApiError.unauthorized("Invalid TOTP code", "INVALID_CODE");
|
|
12982
|
+
}
|
|
12983
|
+
await authRepo.verifyMfaFactor(factorId);
|
|
12984
|
+
return c.json({
|
|
12985
|
+
success: true,
|
|
12986
|
+
message: "MFA factor verified and enrolled"
|
|
12987
|
+
});
|
|
12988
|
+
});
|
|
12989
|
+
router.post("/mfa/challenge", requireAuth, async (c) => {
|
|
12990
|
+
const userCtx = c.get("user");
|
|
12991
|
+
if (!userCtx) {
|
|
12992
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12993
|
+
}
|
|
12994
|
+
const challengeSchema = objectType({
|
|
12995
|
+
factorId: stringType().min(1, "Factor ID is required")
|
|
12996
|
+
});
|
|
12997
|
+
const {
|
|
12998
|
+
factorId
|
|
12999
|
+
} = parseBody2(challengeSchema, await c.req.json());
|
|
13000
|
+
const factor = await authRepo.getMfaFactorById(factorId);
|
|
13001
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
13002
|
+
throw ApiError.notFound("MFA factor not found");
|
|
13003
|
+
}
|
|
13004
|
+
if (!factor.verified) {
|
|
13005
|
+
throw ApiError.badRequest("MFA factor is not yet verified", "FACTOR_NOT_VERIFIED");
|
|
13006
|
+
}
|
|
13007
|
+
const ipAddress = c.req.header("x-forwarded-for") || "unknown";
|
|
13008
|
+
const challenge = await authRepo.createMfaChallenge(factorId, ipAddress);
|
|
13009
|
+
return c.json({
|
|
13010
|
+
challengeId: challenge.id,
|
|
13011
|
+
factorId: challenge.factorId,
|
|
13012
|
+
expiresAt: new Date(Date.now() + 5 * 60 * 1e3).toISOString()
|
|
13013
|
+
});
|
|
13014
|
+
});
|
|
13015
|
+
router.post("/mfa/challenge/verify", requireAuth, async (c) => {
|
|
13016
|
+
const userCtx = c.get("user");
|
|
13017
|
+
if (!userCtx) {
|
|
13018
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
13019
|
+
}
|
|
13020
|
+
const challengeVerifySchema = objectType({
|
|
13021
|
+
challengeId: stringType().min(1, "Challenge ID is required"),
|
|
13022
|
+
code: stringType().min(1, "Code is required")
|
|
13023
|
+
});
|
|
13024
|
+
const {
|
|
13025
|
+
challengeId,
|
|
13026
|
+
code: code2
|
|
13027
|
+
} = parseBody2(challengeVerifySchema, await c.req.json());
|
|
13028
|
+
const challenge = await authRepo.getMfaChallengeById(challengeId);
|
|
13029
|
+
if (!challenge) {
|
|
13030
|
+
throw ApiError.badRequest("Invalid or expired challenge", "INVALID_CHALLENGE");
|
|
13031
|
+
}
|
|
13032
|
+
const factor = await authRepo.getMfaFactorById(challenge.factorId);
|
|
13033
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
13034
|
+
throw ApiError.notFound("MFA factor not found");
|
|
13035
|
+
}
|
|
13036
|
+
const isRecoveryCode = code2.length > 6;
|
|
13037
|
+
let isValid2 = false;
|
|
13038
|
+
if (isRecoveryCode) {
|
|
13039
|
+
const codeHash = hashRecoveryCode(code2);
|
|
13040
|
+
isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
|
|
13041
|
+
} else {
|
|
13042
|
+
const secretBuffer = base32Decode(factor.secretEncrypted);
|
|
13043
|
+
isValid2 = verifyTotp(secretBuffer, code2);
|
|
13044
|
+
}
|
|
13045
|
+
if (!isValid2) {
|
|
13046
|
+
throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
|
|
13047
|
+
}
|
|
13048
|
+
await authRepo.verifyMfaChallenge(challengeId);
|
|
13049
|
+
const roles = await authRepo.getUserRoles(userCtx.userId);
|
|
13050
|
+
const roleIds = roles.map((r) => r.id);
|
|
13051
|
+
const accessToken = generateAccessToken(userCtx.userId, roleIds, "aal2");
|
|
13052
|
+
const refreshToken = generateRefreshToken();
|
|
13053
|
+
await authRepo.createRefreshToken(userCtx.userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
13054
|
+
if (authHooks?.onMfaVerified) {
|
|
13055
|
+
authHooks.onMfaVerified(userCtx.userId, factor.id).catch((err) => {
|
|
13056
|
+
console.error("[AuthHooks] onMfaVerified error:", err instanceof Error ? err.message : err);
|
|
13057
|
+
});
|
|
13058
|
+
}
|
|
13059
|
+
return c.json({
|
|
13060
|
+
tokens: {
|
|
13061
|
+
accessToken,
|
|
13062
|
+
refreshToken,
|
|
13063
|
+
accessTokenExpiresAt: getAccessTokenExpiry()
|
|
13064
|
+
}
|
|
13065
|
+
});
|
|
13066
|
+
});
|
|
13067
|
+
router.get("/mfa/factors", requireAuth, async (c) => {
|
|
13068
|
+
const userCtx = c.get("user");
|
|
13069
|
+
if (!userCtx) {
|
|
13070
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
13071
|
+
}
|
|
13072
|
+
const factors = await authRepo.getMfaFactors(userCtx.userId);
|
|
13073
|
+
return c.json({
|
|
13074
|
+
factors: factors.map((f2) => ({
|
|
13075
|
+
id: f2.id,
|
|
13076
|
+
factorType: f2.factorType,
|
|
13077
|
+
friendlyName: f2.friendlyName,
|
|
13078
|
+
verified: f2.verified,
|
|
13079
|
+
createdAt: f2.createdAt
|
|
13080
|
+
}))
|
|
13081
|
+
});
|
|
13082
|
+
});
|
|
13083
|
+
router.delete("/mfa/unenroll", requireAuth, async (c) => {
|
|
13084
|
+
const userCtx = c.get("user");
|
|
13085
|
+
if (!userCtx) {
|
|
13086
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
13087
|
+
}
|
|
13088
|
+
const unenrollSchema = objectType({
|
|
13089
|
+
factorId: stringType().min(1, "Factor ID is required")
|
|
13090
|
+
});
|
|
13091
|
+
const {
|
|
13092
|
+
factorId
|
|
13093
|
+
} = parseBody2(unenrollSchema, await c.req.json());
|
|
13094
|
+
const factor = await authRepo.getMfaFactorById(factorId);
|
|
13095
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
13096
|
+
throw ApiError.notFound("MFA factor not found");
|
|
13097
|
+
}
|
|
13098
|
+
await authRepo.deleteMfaFactor(factorId, userCtx.userId);
|
|
13099
|
+
const hasFactors = await authRepo.hasVerifiedMfaFactors(userCtx.userId);
|
|
13100
|
+
if (!hasFactors) {
|
|
13101
|
+
await authRepo.deleteAllRecoveryCodes(userCtx.userId);
|
|
13102
|
+
}
|
|
13103
|
+
return c.json({
|
|
13104
|
+
success: true,
|
|
13105
|
+
message: "MFA factor removed"
|
|
13106
|
+
});
|
|
13107
|
+
});
|
|
11884
13108
|
return router;
|
|
11885
13109
|
}
|
|
11886
13110
|
function generateSecurePassword() {
|
|
@@ -11912,9 +13136,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11912
13136
|
emailService,
|
|
11913
13137
|
emailConfig,
|
|
11914
13138
|
hooks,
|
|
11915
|
-
|
|
13139
|
+
authHooks
|
|
11916
13140
|
} = config;
|
|
11917
|
-
const ops =
|
|
13141
|
+
const ops = resolveAuthHooks(authHooks);
|
|
11918
13142
|
function buildHookContext(c, method) {
|
|
11919
13143
|
const user = c.get("user");
|
|
11920
13144
|
return {
|
|
@@ -11934,11 +13158,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11934
13158
|
const results = await Promise.all(users.map((u) => applyUserAfterRead(u, ctx)));
|
|
11935
13159
|
return results.filter((u) => u !== null);
|
|
11936
13160
|
}
|
|
11937
|
-
async function applyRoleAfterReadBatch(roles, ctx) {
|
|
11938
|
-
if (!hooks?.roles?.afterRead) return roles;
|
|
11939
|
-
const results = await Promise.all(roles.map((r) => hooks.roles.afterRead(r, ctx)));
|
|
11940
|
-
return results.filter((r) => r !== null);
|
|
11941
|
-
}
|
|
11942
13161
|
function toAdminUser(u, roles) {
|
|
11943
13162
|
return {
|
|
11944
13163
|
uid: u.id,
|
|
@@ -11982,6 +13201,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11982
13201
|
if (!userId) {
|
|
11983
13202
|
throw ApiError.unauthorized("User ID not found in auth context");
|
|
11984
13203
|
}
|
|
13204
|
+
const caller = await authRepo.getUserById(userId);
|
|
13205
|
+
if (!caller) {
|
|
13206
|
+
throw ApiError.notFound("Authenticated user does not exist in the database. Please sign out and sign in/register again.", "USER_NOT_FOUND");
|
|
13207
|
+
}
|
|
11985
13208
|
await authRepo.setUserRoles(userId, ["admin"]);
|
|
11986
13209
|
if (config.setBootstrapCompleted) {
|
|
11987
13210
|
await config.setBootstrapCompleted();
|
|
@@ -12233,6 +13456,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12233
13456
|
await authRepo.updateUser(userId, updates);
|
|
12234
13457
|
}
|
|
12235
13458
|
if (roles !== void 0 && Array.isArray(roles)) {
|
|
13459
|
+
const currentRoles = await authRepo.getUserRoleIds(userId);
|
|
13460
|
+
const wasAdmin = currentRoles.includes("admin");
|
|
13461
|
+
const willBeAdmin = roles.includes("admin");
|
|
13462
|
+
if (wasAdmin && !willBeAdmin) {
|
|
13463
|
+
const adminUsers = await authRepo.listUsersPaginated({
|
|
13464
|
+
roleId: "admin",
|
|
13465
|
+
limit: 1
|
|
13466
|
+
});
|
|
13467
|
+
if (adminUsers.total <= 1) {
|
|
13468
|
+
throw ApiError.forbidden("Cannot demote the last administrator", "LAST_ADMIN");
|
|
13469
|
+
}
|
|
13470
|
+
}
|
|
12236
13471
|
await authRepo.setUserRoles(userId, roles);
|
|
12237
13472
|
}
|
|
12238
13473
|
const result = await authRepo.getUserWithRoles(userId);
|
|
@@ -12257,6 +13492,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12257
13492
|
if (!existing) {
|
|
12258
13493
|
throw ApiError.notFound("User not found");
|
|
12259
13494
|
}
|
|
13495
|
+
const roles = await authRepo.getUserRoleIds(userId);
|
|
13496
|
+
if (roles.includes("admin")) {
|
|
13497
|
+
const adminUsers = await authRepo.listUsersPaginated({
|
|
13498
|
+
roleId: "admin",
|
|
13499
|
+
limit: 1
|
|
13500
|
+
});
|
|
13501
|
+
if (adminUsers.total <= 1) {
|
|
13502
|
+
throw ApiError.forbidden("Cannot delete the last administrator", "LAST_ADMIN");
|
|
13503
|
+
}
|
|
13504
|
+
}
|
|
12260
13505
|
const hookCtx = buildHookContext(c, "DELETE");
|
|
12261
13506
|
if (hooks?.users?.beforeDelete) {
|
|
12262
13507
|
await hooks.users.beforeDelete(userId, hookCtx);
|
|
@@ -12271,95 +13516,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12271
13516
|
success: true
|
|
12272
13517
|
});
|
|
12273
13518
|
});
|
|
12274
|
-
router.get("/roles", requireAdmin, async (c) => {
|
|
12275
|
-
const roles = await authRepo.listRoles();
|
|
12276
|
-
const hookCtx = buildHookContext(c, "GET");
|
|
12277
|
-
let adminRoles = roles.map((r) => ({
|
|
12278
|
-
id: r.id,
|
|
12279
|
-
name: r.name,
|
|
12280
|
-
isAdmin: r.isAdmin,
|
|
12281
|
-
defaultPermissions: r.defaultPermissions,
|
|
12282
|
-
config: r.config
|
|
12283
|
-
}));
|
|
12284
|
-
adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
|
|
12285
|
-
return c.json({
|
|
12286
|
-
roles: adminRoles
|
|
12287
|
-
});
|
|
12288
|
-
});
|
|
12289
|
-
router.get("/roles/:roleId", requireAdmin, async (c) => {
|
|
12290
|
-
const roleId = c.req.param("roleId");
|
|
12291
|
-
const role = await authRepo.getRoleById(roleId);
|
|
12292
|
-
if (!role) {
|
|
12293
|
-
throw ApiError.notFound("Role not found");
|
|
12294
|
-
}
|
|
12295
|
-
return c.json({
|
|
12296
|
-
role
|
|
12297
|
-
});
|
|
12298
|
-
});
|
|
12299
|
-
router.post("/roles", requireAdmin, async (c) => {
|
|
12300
|
-
const body = await c.req.json();
|
|
12301
|
-
const {
|
|
12302
|
-
id,
|
|
12303
|
-
name: name2,
|
|
12304
|
-
isAdmin,
|
|
12305
|
-
defaultPermissions,
|
|
12306
|
-
config: config2
|
|
12307
|
-
} = body;
|
|
12308
|
-
if (!id || !name2) {
|
|
12309
|
-
throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
|
|
12310
|
-
}
|
|
12311
|
-
const existing = await authRepo.getRoleById(id);
|
|
12312
|
-
if (existing) {
|
|
12313
|
-
throw ApiError.conflict("Role already exists", "ROLE_EXISTS");
|
|
12314
|
-
}
|
|
12315
|
-
const role = await authRepo.createRole({
|
|
12316
|
-
id,
|
|
12317
|
-
name: name2,
|
|
12318
|
-
isAdmin: isAdmin ?? false,
|
|
12319
|
-
defaultPermissions: defaultPermissions ?? null,
|
|
12320
|
-
config: config2 ?? null
|
|
12321
|
-
});
|
|
12322
|
-
return c.json({
|
|
12323
|
-
role
|
|
12324
|
-
}, 201);
|
|
12325
|
-
});
|
|
12326
|
-
router.put("/roles/:roleId", requireAdmin, async (c) => {
|
|
12327
|
-
const roleId = c.req.param("roleId");
|
|
12328
|
-
const body = await c.req.json();
|
|
12329
|
-
const {
|
|
12330
|
-
name: name2,
|
|
12331
|
-
isAdmin,
|
|
12332
|
-
defaultPermissions,
|
|
12333
|
-
config: config2
|
|
12334
|
-
} = body;
|
|
12335
|
-
const existing = await authRepo.getRoleById(roleId);
|
|
12336
|
-
if (!existing) {
|
|
12337
|
-
throw ApiError.notFound("Role not found");
|
|
12338
|
-
}
|
|
12339
|
-
const role = await authRepo.updateRole(roleId, {
|
|
12340
|
-
name: name2,
|
|
12341
|
-
isAdmin,
|
|
12342
|
-
defaultPermissions,
|
|
12343
|
-
config: config2
|
|
12344
|
-
});
|
|
12345
|
-
return c.json({
|
|
12346
|
-
role
|
|
12347
|
-
});
|
|
12348
|
-
});
|
|
12349
|
-
router.delete("/roles/:roleId", requireAdmin, async (c) => {
|
|
12350
|
-
const roleId = c.req.param("roleId");
|
|
12351
|
-
if (["admin", "editor", "viewer"].includes(roleId)) {
|
|
12352
|
-
throw ApiError.badRequest("Cannot delete built-in roles", "BUILTIN_ROLE");
|
|
12353
|
-
}
|
|
12354
|
-
const existing = await authRepo.getRoleById(roleId);
|
|
12355
|
-
if (!existing) {
|
|
12356
|
-
throw ApiError.notFound("Role not found");
|
|
12357
|
-
}
|
|
12358
|
-
await authRepo.deleteRole(roleId);
|
|
12359
|
-
return c.json({
|
|
12360
|
-
success: true
|
|
12361
|
-
});
|
|
12362
|
-
});
|
|
12363
13519
|
return router;
|
|
12364
13520
|
}
|
|
12365
13521
|
function createBuiltinAuthAdapter(config) {
|
|
@@ -12372,9 +13528,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12372
13528
|
oauthProviders = [],
|
|
12373
13529
|
serviceKey,
|
|
12374
13530
|
hooks,
|
|
12375
|
-
|
|
13531
|
+
authHooks
|
|
12376
13532
|
} = config;
|
|
12377
|
-
const resolvedOps =
|
|
13533
|
+
const resolvedOps = resolveAuthHooks(authHooks);
|
|
12378
13534
|
const adapter = {
|
|
12379
13535
|
id: "rebase-builtin",
|
|
12380
13536
|
serviceKey,
|
|
@@ -12403,8 +13559,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12403
13559
|
const extendedPayload = payload;
|
|
12404
13560
|
let roles = payload.roles || [];
|
|
12405
13561
|
try {
|
|
12406
|
-
|
|
12407
|
-
roles = userRoles.map((r) => r.id);
|
|
13562
|
+
roles = await authRepository.getUserRoleIds(payload.userId);
|
|
12408
13563
|
} catch {
|
|
12409
13564
|
}
|
|
12410
13565
|
const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
|
|
@@ -12434,8 +13589,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12434
13589
|
const extendedPayload = payload;
|
|
12435
13590
|
let roles = payload.roles || [];
|
|
12436
13591
|
try {
|
|
12437
|
-
|
|
12438
|
-
roles = userRoles.map((r) => r.id);
|
|
13592
|
+
roles = await authRepository.getUserRoleIds(payload.userId);
|
|
12439
13593
|
} catch {
|
|
12440
13594
|
}
|
|
12441
13595
|
const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
|
|
@@ -12448,8 +13602,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12448
13602
|
rawToken: token
|
|
12449
13603
|
};
|
|
12450
13604
|
},
|
|
12451
|
-
userManagement: createUserManagementFromRepo(authRepository, resolvedOps,
|
|
12452
|
-
roleManagement: createRoleManagementFromRepo(authRepository),
|
|
13605
|
+
userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
|
|
12453
13606
|
createAuthRoutes() {
|
|
12454
13607
|
return createAuthRoutes({
|
|
12455
13608
|
authRepo: authRepository,
|
|
@@ -12458,7 +13611,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12458
13611
|
allowRegistration,
|
|
12459
13612
|
defaultRole,
|
|
12460
13613
|
oauthProviders,
|
|
12461
|
-
|
|
13614
|
+
authHooks
|
|
12462
13615
|
});
|
|
12463
13616
|
},
|
|
12464
13617
|
createAdminRoutes() {
|
|
@@ -12468,7 +13621,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12468
13621
|
emailConfig,
|
|
12469
13622
|
serviceKey,
|
|
12470
13623
|
hooks,
|
|
12471
|
-
|
|
13624
|
+
authHooks
|
|
12472
13625
|
});
|
|
12473
13626
|
},
|
|
12474
13627
|
async getCapabilities() {
|
|
@@ -12497,7 +13650,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12497
13650
|
};
|
|
12498
13651
|
return adapter;
|
|
12499
13652
|
}
|
|
12500
|
-
function createUserManagementFromRepo(repo, resolvedOps,
|
|
13653
|
+
function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
|
|
12501
13654
|
return {
|
|
12502
13655
|
async listUsers(options2) {
|
|
12503
13656
|
const result = await repo.listUsersPaginated({
|
|
@@ -12528,14 +13681,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12528
13681
|
photoUrl: data.photoUrl,
|
|
12529
13682
|
metadata: data.metadata
|
|
12530
13683
|
};
|
|
12531
|
-
if (
|
|
12532
|
-
createData = await
|
|
13684
|
+
if (authHooks?.beforeUserCreate) {
|
|
13685
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
12533
13686
|
}
|
|
12534
13687
|
const user = await repo.createUser(createData);
|
|
12535
|
-
if (
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
})
|
|
13688
|
+
if (authHooks?.afterUserCreate) {
|
|
13689
|
+
try {
|
|
13690
|
+
await authHooks.afterUserCreate(user);
|
|
13691
|
+
} catch (err) {
|
|
13692
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
13693
|
+
}
|
|
12539
13694
|
}
|
|
12540
13695
|
return toAuthUserData(user);
|
|
12541
13696
|
},
|
|
@@ -12552,47 +13707,24 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12552
13707
|
return user ? toAuthUserData(user) : null;
|
|
12553
13708
|
},
|
|
12554
13709
|
async deleteUser(id) {
|
|
13710
|
+
if (authHooks?.beforeUserDelete) {
|
|
13711
|
+
await authHooks.beforeUserDelete(id);
|
|
13712
|
+
}
|
|
12555
13713
|
await repo.deleteUser(id);
|
|
13714
|
+
if (authHooks?.afterUserDelete) {
|
|
13715
|
+
authHooks.afterUserDelete(id).catch((err) => {
|
|
13716
|
+
console.error("[AuthHooks] afterUserDelete error:", err instanceof Error ? err.message : err);
|
|
13717
|
+
});
|
|
13718
|
+
}
|
|
12556
13719
|
},
|
|
12557
13720
|
async getUserRoles(userId) {
|
|
12558
|
-
|
|
12559
|
-
return roles.map(toAuthRoleData);
|
|
13721
|
+
return repo.getUserRoleIds(userId);
|
|
12560
13722
|
},
|
|
12561
13723
|
async setUserRoles(userId, roleIds) {
|
|
12562
13724
|
await repo.setUserRoles(userId, roleIds);
|
|
12563
13725
|
}
|
|
12564
13726
|
};
|
|
12565
13727
|
}
|
|
12566
|
-
function createRoleManagementFromRepo(repo) {
|
|
12567
|
-
return {
|
|
12568
|
-
async listRoles() {
|
|
12569
|
-
const roles = await repo.listRoles();
|
|
12570
|
-
return roles.map(toAuthRoleData);
|
|
12571
|
-
},
|
|
12572
|
-
async getRoleById(id) {
|
|
12573
|
-
const role = await repo.getRoleById(id);
|
|
12574
|
-
return role ? toAuthRoleData(role) : null;
|
|
12575
|
-
},
|
|
12576
|
-
async createRole(data) {
|
|
12577
|
-
const role = await repo.createRole({
|
|
12578
|
-
id: data.id,
|
|
12579
|
-
name: data.name,
|
|
12580
|
-
isAdmin: data.isAdmin,
|
|
12581
|
-
defaultPermissions: data.defaultPermissions,
|
|
12582
|
-
collectionPermissions: data.collectionPermissions,
|
|
12583
|
-
config: data.config
|
|
12584
|
-
});
|
|
12585
|
-
return toAuthRoleData(role);
|
|
12586
|
-
},
|
|
12587
|
-
async updateRole(id, data) {
|
|
12588
|
-
const role = await repo.updateRole(id, data);
|
|
12589
|
-
return role ? toAuthRoleData(role) : null;
|
|
12590
|
-
},
|
|
12591
|
-
async deleteRole(id) {
|
|
12592
|
-
await repo.deleteRole(id);
|
|
12593
|
-
}
|
|
12594
|
-
};
|
|
12595
|
-
}
|
|
12596
13728
|
function toAuthUserData(user) {
|
|
12597
13729
|
return {
|
|
12598
13730
|
id: user.id,
|
|
@@ -12605,16 +13737,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12605
13737
|
updatedAt: user.updatedAt
|
|
12606
13738
|
};
|
|
12607
13739
|
}
|
|
12608
|
-
function toAuthRoleData(role) {
|
|
12609
|
-
return {
|
|
12610
|
-
id: role.id,
|
|
12611
|
-
name: role.name,
|
|
12612
|
-
isAdmin: role.isAdmin,
|
|
12613
|
-
defaultPermissions: role.defaultPermissions,
|
|
12614
|
-
collectionPermissions: role.collectionPermissions,
|
|
12615
|
-
config: role.config
|
|
12616
|
-
};
|
|
12617
|
-
}
|
|
12618
13740
|
function configureLogLevel(logLevel) {
|
|
12619
13741
|
const LOG_LEVEL = logLevel || process.env.LOG_LEVEL || "info";
|
|
12620
13742
|
const logLevels = {
|
|
@@ -12633,20 +13755,20 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12633
13755
|
if (currentLevel < 0) console.error = () => {
|
|
12634
13756
|
};
|
|
12635
13757
|
}
|
|
13758
|
+
let originalConsole;
|
|
12636
13759
|
function resetConsole() {
|
|
12637
|
-
if (!
|
|
12638
|
-
|
|
13760
|
+
if (!originalConsole) {
|
|
13761
|
+
originalConsole = {
|
|
12639
13762
|
log: console.log,
|
|
12640
13763
|
warn: console.warn,
|
|
12641
13764
|
error: console.error,
|
|
12642
13765
|
debug: console.debug
|
|
12643
13766
|
};
|
|
12644
13767
|
}
|
|
12645
|
-
|
|
12646
|
-
console.
|
|
12647
|
-
console.
|
|
12648
|
-
console.
|
|
12649
|
-
console.debug = original.debug;
|
|
13768
|
+
console.log = originalConsole.log;
|
|
13769
|
+
console.warn = originalConsole.warn;
|
|
13770
|
+
console.error = originalConsole.error;
|
|
13771
|
+
console.debug = originalConsole.debug;
|
|
12650
13772
|
}
|
|
12651
13773
|
const GCP_SEVERITY = {
|
|
12652
13774
|
debug: "DEBUG",
|
|
@@ -12888,12 +14010,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12888
14010
|
exports3.Response = globalObject.Response;
|
|
12889
14011
|
})(browser$1, browser$1.exports);
|
|
12890
14012
|
var browserExports = browser$1.exports;
|
|
12891
|
-
const __viteBrowserExternal = {};
|
|
12892
|
-
const __viteBrowserExternal$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
12893
|
-
__proto__: null,
|
|
12894
|
-
default: __viteBrowserExternal
|
|
12895
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
12896
|
-
const require$$11 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
|
|
12897
14013
|
const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
|
|
12898
14014
|
isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
|
|
12899
14015
|
isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
|
|
@@ -13678,16 +14794,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13678
14794
|
value: true
|
|
13679
14795
|
});
|
|
13680
14796
|
sha1$1.default = void 0;
|
|
13681
|
-
function f(s2, x, y2,
|
|
14797
|
+
function f(s2, x, y2, z2) {
|
|
13682
14798
|
switch (s2) {
|
|
13683
14799
|
case 0:
|
|
13684
|
-
return x & y2 ^ ~x &
|
|
14800
|
+
return x & y2 ^ ~x & z2;
|
|
13685
14801
|
case 1:
|
|
13686
|
-
return x ^ y2 ^
|
|
14802
|
+
return x ^ y2 ^ z2;
|
|
13687
14803
|
case 2:
|
|
13688
|
-
return x & y2 ^ x &
|
|
14804
|
+
return x & y2 ^ x & z2 ^ y2 & z2;
|
|
13689
14805
|
case 3:
|
|
13690
|
-
return x ^ y2 ^
|
|
14806
|
+
return x ^ y2 ^ z2;
|
|
13691
14807
|
}
|
|
13692
14808
|
}
|
|
13693
14809
|
function ROTL(x, n) {
|
|
@@ -14736,7 +15852,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
14736
15852
|
const extend_1 = __importDefault(extend);
|
|
14737
15853
|
const https_1 = require$$1;
|
|
14738
15854
|
const node_fetch_1 = __importDefault(browserExports);
|
|
14739
|
-
const querystring_1$1 = __importDefault(require$$
|
|
15855
|
+
const querystring_1$1 = __importDefault(require$$1$2);
|
|
14740
15856
|
const is_stream_1 = __importDefault(isStream_1);
|
|
14741
15857
|
const url_1 = require$$0$2;
|
|
14742
15858
|
const common_1 = common$1;
|
|
@@ -16414,11 +17530,11 @@ Content-Type: ${partContentType}\r
|
|
|
16414
17530
|
return n > 0 || n === i2 ? i2 : i2 - 1;
|
|
16415
17531
|
}
|
|
16416
17532
|
function coeffToString(a2) {
|
|
16417
|
-
var s2,
|
|
17533
|
+
var s2, z2, i2 = 1, j = a2.length, r = a2[0] + "";
|
|
16418
17534
|
for (; i2 < j; ) {
|
|
16419
17535
|
s2 = a2[i2++] + "";
|
|
16420
|
-
|
|
16421
|
-
for (;
|
|
17536
|
+
z2 = LOG_BASE - s2.length;
|
|
17537
|
+
for (; z2--; s2 = "0" + s2) ;
|
|
16422
17538
|
r += s2;
|
|
16423
17539
|
}
|
|
16424
17540
|
for (j = r.length; r.charCodeAt(--j) === 48; ) ;
|
|
@@ -16451,15 +17567,15 @@ Content-Type: ${partContentType}\r
|
|
|
16451
17567
|
function toExponential(str, e) {
|
|
16452
17568
|
return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
|
|
16453
17569
|
}
|
|
16454
|
-
function toFixedPoint(str, e,
|
|
17570
|
+
function toFixedPoint(str, e, z2) {
|
|
16455
17571
|
var len2, zs;
|
|
16456
17572
|
if (e < 0) {
|
|
16457
|
-
for (zs =
|
|
17573
|
+
for (zs = z2 + "."; ++e; zs += z2) ;
|
|
16458
17574
|
str = zs + str;
|
|
16459
17575
|
} else {
|
|
16460
17576
|
len2 = str.length;
|
|
16461
17577
|
if (++e > len2) {
|
|
16462
|
-
for (zs =
|
|
17578
|
+
for (zs = z2, e -= len2; --e; zs += z2) ;
|
|
16463
17579
|
str += zs;
|
|
16464
17580
|
} else if (e < len2) {
|
|
16465
17581
|
str = str.slice(0, e) + "." + str.slice(e);
|
|
@@ -16878,7 +17994,7 @@ Content-Type: ${partContentType}\r
|
|
|
16878
17994
|
exports3.isGoogleComputeEngine = isGoogleComputeEngine;
|
|
16879
17995
|
exports3.detectGCPResidency = detectGCPResidency;
|
|
16880
17996
|
const fs_1 = fs$4;
|
|
16881
|
-
const os_1 = require$$1$
|
|
17997
|
+
const os_1 = require$$1$3;
|
|
16882
17998
|
exports3.GCE_LINUX_BIOS_PATHS = {
|
|
16883
17999
|
BIOS_DATE: "/sys/class/dmi/id/bios_date",
|
|
16884
18000
|
BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
|
|
@@ -17011,7 +18127,7 @@ Content-Type: ${partContentType}\r
|
|
|
17011
18127
|
exports3.setBackend = setBackend;
|
|
17012
18128
|
exports3.log = log;
|
|
17013
18129
|
const node_events_1 = require$$0$8;
|
|
17014
|
-
const process2 = __importStar2(require$$1$
|
|
18130
|
+
const process2 = __importStar2(require$$1$4);
|
|
17015
18131
|
const util2 = __importStar2(require$$2$1);
|
|
17016
18132
|
const colours_1 = colours;
|
|
17017
18133
|
var LogSeverity;
|
|
@@ -18083,7 +19199,7 @@ Content-Type: ${partContentType}\r
|
|
|
18083
19199
|
Object.defineProperty(oauth2client, "__esModule", { value: true });
|
|
18084
19200
|
oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
|
|
18085
19201
|
const gaxios_1$5 = src$2;
|
|
18086
|
-
const querystring$2 = require$$
|
|
19202
|
+
const querystring$2 = require$$1$2;
|
|
18087
19203
|
const stream$4 = require$$0$4;
|
|
18088
19204
|
const formatEcdsa = ecdsaSigFormatter;
|
|
18089
19205
|
const crypto_1$2 = requireCrypto();
|
|
@@ -19571,7 +20687,7 @@ Content-Type: ${partContentType}\r
|
|
|
19571
20687
|
Object.defineProperty(refreshclient, "__esModule", { value: true });
|
|
19572
20688
|
refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
|
|
19573
20689
|
const oauth2client_1$1 = oauth2client;
|
|
19574
|
-
const querystring_1 = require$$
|
|
20690
|
+
const querystring_1 = require$$1$2;
|
|
19575
20691
|
refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
|
|
19576
20692
|
class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
|
|
19577
20693
|
constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
|
|
@@ -19846,7 +20962,7 @@ Content-Type: ${partContentType}\r
|
|
|
19846
20962
|
Object.defineProperty(oauth2common, "__esModule", { value: true });
|
|
19847
20963
|
oauth2common.OAuthClientAuthHandler = void 0;
|
|
19848
20964
|
oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
|
|
19849
|
-
const querystring$1 = require$$
|
|
20965
|
+
const querystring$1 = require$$1$2;
|
|
19850
20966
|
const crypto_1$1 = requireCrypto();
|
|
19851
20967
|
const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
|
|
19852
20968
|
class OAuthClientAuthHandler {
|
|
@@ -19992,7 +21108,7 @@ Content-Type: ${partContentType}\r
|
|
|
19992
21108
|
Object.defineProperty(stscredentials, "__esModule", { value: true });
|
|
19993
21109
|
stscredentials.StsCredentials = void 0;
|
|
19994
21110
|
const gaxios_1$1 = src$2;
|
|
19995
|
-
const querystring = require$$
|
|
21111
|
+
const querystring = require$$1$2;
|
|
19996
21112
|
const transporters_1 = transporters;
|
|
19997
21113
|
const oauth2common_1$1 = oauth2common;
|
|
19998
21114
|
class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
|
|
@@ -21598,7 +22714,7 @@ ${credentialScope}
|
|
|
21598
22714
|
const child_process_1 = require$$0$a;
|
|
21599
22715
|
const fs2 = fs$4;
|
|
21600
22716
|
const gcpMetadata2 = src$3;
|
|
21601
|
-
const os2 = require$$1$
|
|
22717
|
+
const os2 = require$$1$3;
|
|
21602
22718
|
const path2 = path$3;
|
|
21603
22719
|
const crypto_12 = requireCrypto();
|
|
21604
22720
|
const transporters_12 = transporters;
|
|
@@ -22962,7 +24078,7 @@ ${credentialScope}
|
|
|
22962
24078
|
}
|
|
22963
24079
|
function createAppleProvider(config) {
|
|
22964
24080
|
async function generateClientSecret() {
|
|
22965
|
-
return jwt.sign({}, config.privateKey, {
|
|
24081
|
+
return jwt$1.sign({}, config.privateKey, {
|
|
22966
24082
|
algorithm: "ES256",
|
|
22967
24083
|
keyid: config.keyId,
|
|
22968
24084
|
issuer: config.teamId,
|
|
@@ -23442,6 +24558,341 @@ ${credentialScope}
|
|
|
23442
24558
|
}
|
|
23443
24559
|
};
|
|
23444
24560
|
}
|
|
24561
|
+
const TABLE$1 = "rebase.api_keys";
|
|
24562
|
+
function generateApiKey() {
|
|
24563
|
+
const random = require$$0$5.randomBytes(16).toString("hex");
|
|
24564
|
+
return `rk_live_${random}`;
|
|
24565
|
+
}
|
|
24566
|
+
function hashKey(plaintext) {
|
|
24567
|
+
return require$$0$5.createHash("sha256").update(plaintext).digest("hex");
|
|
24568
|
+
}
|
|
24569
|
+
function keyPrefix(plaintext) {
|
|
24570
|
+
return plaintext.substring(0, 12);
|
|
24571
|
+
}
|
|
24572
|
+
function toMasked(row) {
|
|
24573
|
+
return {
|
|
24574
|
+
id: row.id,
|
|
24575
|
+
name: row.name,
|
|
24576
|
+
key_prefix: row.key_prefix,
|
|
24577
|
+
permissions: row.permissions,
|
|
24578
|
+
rate_limit: row.rate_limit,
|
|
24579
|
+
created_by: row.created_by,
|
|
24580
|
+
created_at: row.created_at,
|
|
24581
|
+
updated_at: row.updated_at,
|
|
24582
|
+
last_used_at: row.last_used_at,
|
|
24583
|
+
expires_at: row.expires_at,
|
|
24584
|
+
revoked_at: row.revoked_at
|
|
24585
|
+
};
|
|
24586
|
+
}
|
|
24587
|
+
function rowToApiKey(row) {
|
|
24588
|
+
const permissions = typeof row.permissions === "string" ? JSON.parse(row.permissions) : row.permissions ?? [];
|
|
24589
|
+
return {
|
|
24590
|
+
id: row.id,
|
|
24591
|
+
name: row.name,
|
|
24592
|
+
key_prefix: row.key_prefix,
|
|
24593
|
+
key_hash: row.key_hash,
|
|
24594
|
+
permissions,
|
|
24595
|
+
rate_limit: row.rate_limit !== null && row.rate_limit !== void 0 ? Number(row.rate_limit) : null,
|
|
24596
|
+
created_by: row.created_by,
|
|
24597
|
+
created_at: new Date(row.created_at).toISOString(),
|
|
24598
|
+
updated_at: new Date(row.updated_at).toISOString(),
|
|
24599
|
+
last_used_at: row.last_used_at ? new Date(row.last_used_at).toISOString() : null,
|
|
24600
|
+
expires_at: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
24601
|
+
revoked_at: row.revoked_at ? new Date(row.revoked_at).toISOString() : null
|
|
24602
|
+
};
|
|
24603
|
+
}
|
|
24604
|
+
function esc(value) {
|
|
24605
|
+
return value.replace(/'/g, "''");
|
|
24606
|
+
}
|
|
24607
|
+
function createApiKeyStore(driver) {
|
|
24608
|
+
const admin = driver.admin;
|
|
24609
|
+
if (!isSQLAdmin(admin)) {
|
|
24610
|
+
logger.warn("⚠️ [api-key-store] DataDriver does not support SQL admin — API keys will not be available.");
|
|
24611
|
+
return void 0;
|
|
24612
|
+
}
|
|
24613
|
+
const exec = admin.executeSql.bind(admin);
|
|
24614
|
+
return {
|
|
24615
|
+
// ── Schema bootstrap ────────────────────────────────────────
|
|
24616
|
+
async ensureTable() {
|
|
24617
|
+
try {
|
|
24618
|
+
await exec("CREATE SCHEMA IF NOT EXISTS rebase");
|
|
24619
|
+
await exec(`
|
|
24620
|
+
CREATE TABLE IF NOT EXISTS ${TABLE$1} (
|
|
24621
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
24622
|
+
name TEXT NOT NULL,
|
|
24623
|
+
key_prefix TEXT NOT NULL,
|
|
24624
|
+
key_hash TEXT NOT NULL UNIQUE,
|
|
24625
|
+
permissions JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
24626
|
+
rate_limit INTEGER,
|
|
24627
|
+
created_by TEXT NOT NULL,
|
|
24628
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
24629
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
24630
|
+
last_used_at TIMESTAMPTZ,
|
|
24631
|
+
expires_at TIMESTAMPTZ,
|
|
24632
|
+
revoked_at TIMESTAMPTZ
|
|
24633
|
+
)
|
|
24634
|
+
`);
|
|
24635
|
+
await exec(`
|
|
24636
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_hash
|
|
24637
|
+
ON ${TABLE$1}(key_hash)
|
|
24638
|
+
`);
|
|
24639
|
+
await exec(`
|
|
24640
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_prefix
|
|
24641
|
+
ON ${TABLE$1}(key_prefix)
|
|
24642
|
+
`);
|
|
24643
|
+
logger.info("✅ API keys table ready");
|
|
24644
|
+
} catch (err) {
|
|
24645
|
+
logger.error("❌ Failed to create API keys table", {
|
|
24646
|
+
error: err
|
|
24647
|
+
});
|
|
24648
|
+
logger.warn("⚠️ Continuing without API keys support.");
|
|
24649
|
+
}
|
|
24650
|
+
},
|
|
24651
|
+
// ── Create ──────────────────────────────────────────────────
|
|
24652
|
+
async createApiKey(request, createdBy) {
|
|
24653
|
+
const plaintext = generateApiKey();
|
|
24654
|
+
const hash = hashKey(plaintext);
|
|
24655
|
+
const prefix = keyPrefix(plaintext);
|
|
24656
|
+
const permissionsJson = JSON.stringify(request.permissions);
|
|
24657
|
+
const rateLimitSql = request.rate_limit !== null && request.rate_limit !== void 0 ? String(request.rate_limit) : "NULL";
|
|
24658
|
+
const expiresAtSql = request.expires_at ? `'${esc(request.expires_at)}'` : "NULL";
|
|
24659
|
+
const rows = await exec(`
|
|
24660
|
+
INSERT INTO ${TABLE$1} (name, key_prefix, key_hash, permissions, rate_limit, created_by, expires_at)
|
|
24661
|
+
VALUES (
|
|
24662
|
+
'${esc(request.name)}',
|
|
24663
|
+
'${esc(prefix)}',
|
|
24664
|
+
'${esc(hash)}',
|
|
24665
|
+
'${esc(permissionsJson)}'::jsonb,
|
|
24666
|
+
${rateLimitSql},
|
|
24667
|
+
'${esc(createdBy)}',
|
|
24668
|
+
${expiresAtSql}
|
|
24669
|
+
)
|
|
24670
|
+
RETURNING *
|
|
24671
|
+
`);
|
|
24672
|
+
const apiKey = rowToApiKey(rows[0]);
|
|
24673
|
+
return {
|
|
24674
|
+
...toMasked(apiKey),
|
|
24675
|
+
key: plaintext
|
|
24676
|
+
};
|
|
24677
|
+
},
|
|
24678
|
+
// ── Lookup by hash ──────────────────────────────────────────
|
|
24679
|
+
async findByKeyHash(hash) {
|
|
24680
|
+
const rows = await exec(`
|
|
24681
|
+
SELECT * FROM ${TABLE$1}
|
|
24682
|
+
WHERE key_hash = '${esc(hash)}'
|
|
24683
|
+
LIMIT 1
|
|
24684
|
+
`);
|
|
24685
|
+
if (rows.length === 0) return null;
|
|
24686
|
+
return rowToApiKey(rows[0]);
|
|
24687
|
+
},
|
|
24688
|
+
// ── List all (masked) ───────────────────────────────────────
|
|
24689
|
+
async listApiKeys() {
|
|
24690
|
+
const rows = await exec(`
|
|
24691
|
+
SELECT * FROM ${TABLE$1}
|
|
24692
|
+
ORDER BY created_at DESC
|
|
24693
|
+
`);
|
|
24694
|
+
return rows.map((r) => toMasked(rowToApiKey(r)));
|
|
24695
|
+
},
|
|
24696
|
+
// ── Get by ID (masked) ──────────────────────────────────────
|
|
24697
|
+
async getApiKeyById(id) {
|
|
24698
|
+
const rows = await exec(`
|
|
24699
|
+
SELECT * FROM ${TABLE$1}
|
|
24700
|
+
WHERE id = '${esc(id)}'
|
|
24701
|
+
LIMIT 1
|
|
24702
|
+
`);
|
|
24703
|
+
if (rows.length === 0) return null;
|
|
24704
|
+
return toMasked(rowToApiKey(rows[0]));
|
|
24705
|
+
},
|
|
24706
|
+
// ── Update ──────────────────────────────────────────────────
|
|
24707
|
+
async updateApiKey(id, updates) {
|
|
24708
|
+
const setClauses = [];
|
|
24709
|
+
if (updates.name !== void 0) {
|
|
24710
|
+
setClauses.push(`name = '${esc(updates.name)}'`);
|
|
24711
|
+
}
|
|
24712
|
+
if (updates.permissions !== void 0) {
|
|
24713
|
+
setClauses.push(`permissions = '${esc(JSON.stringify(updates.permissions))}'::jsonb`);
|
|
24714
|
+
}
|
|
24715
|
+
if (updates.rate_limit !== void 0) {
|
|
24716
|
+
setClauses.push(updates.rate_limit !== null ? `rate_limit = ${updates.rate_limit}` : "rate_limit = NULL");
|
|
24717
|
+
}
|
|
24718
|
+
if (updates.expires_at !== void 0) {
|
|
24719
|
+
setClauses.push(updates.expires_at !== null ? `expires_at = '${esc(updates.expires_at)}'` : "expires_at = NULL");
|
|
24720
|
+
}
|
|
24721
|
+
if (setClauses.length === 0) {
|
|
24722
|
+
return this.getApiKeyById(id);
|
|
24723
|
+
}
|
|
24724
|
+
setClauses.push("updated_at = NOW()");
|
|
24725
|
+
const rows = await exec(`
|
|
24726
|
+
UPDATE ${TABLE$1}
|
|
24727
|
+
SET ${setClauses.join(", ")}
|
|
24728
|
+
WHERE id = '${esc(id)}'
|
|
24729
|
+
RETURNING *
|
|
24730
|
+
`);
|
|
24731
|
+
if (rows.length === 0) return null;
|
|
24732
|
+
return toMasked(rowToApiKey(rows[0]));
|
|
24733
|
+
},
|
|
24734
|
+
// ── Revoke (soft-delete) ────────────────────────────────────
|
|
24735
|
+
async revokeApiKey(id) {
|
|
24736
|
+
const rows = await exec(`
|
|
24737
|
+
UPDATE ${TABLE$1}
|
|
24738
|
+
SET revoked_at = NOW(), updated_at = NOW()
|
|
24739
|
+
WHERE id = '${esc(id)}' AND revoked_at IS NULL
|
|
24740
|
+
RETURNING id
|
|
24741
|
+
`);
|
|
24742
|
+
return rows.length > 0;
|
|
24743
|
+
},
|
|
24744
|
+
// ── Touch last_used_at ──────────────────────────────────────
|
|
24745
|
+
async updateLastUsed(id) {
|
|
24746
|
+
try {
|
|
24747
|
+
await exec(`
|
|
24748
|
+
UPDATE ${TABLE$1}
|
|
24749
|
+
SET last_used_at = NOW()
|
|
24750
|
+
WHERE id = '${esc(id)}'
|
|
24751
|
+
`);
|
|
24752
|
+
} catch (err) {
|
|
24753
|
+
logger.error("[api-key-store] Failed to update last_used_at", {
|
|
24754
|
+
error: err
|
|
24755
|
+
});
|
|
24756
|
+
}
|
|
24757
|
+
}
|
|
24758
|
+
};
|
|
24759
|
+
}
|
|
24760
|
+
function validatePermissions(permissions) {
|
|
24761
|
+
if (!Array.isArray(permissions)) return false;
|
|
24762
|
+
for (const perm of permissions) {
|
|
24763
|
+
if (typeof perm !== "object" || perm === null) return false;
|
|
24764
|
+
if (typeof perm.collection !== "string" || perm.collection.length === 0) return false;
|
|
24765
|
+
if (!Array.isArray(perm.operations) || perm.operations.length === 0) return false;
|
|
24766
|
+
const validOps = /* @__PURE__ */ new Set(["read", "write", "delete"]);
|
|
24767
|
+
for (const op of perm.operations) {
|
|
24768
|
+
if (!validOps.has(op)) return false;
|
|
24769
|
+
}
|
|
24770
|
+
}
|
|
24771
|
+
return true;
|
|
24772
|
+
}
|
|
24773
|
+
function createApiKeyRoutes(options2) {
|
|
24774
|
+
const {
|
|
24775
|
+
store,
|
|
24776
|
+
serviceKey
|
|
24777
|
+
} = options2;
|
|
24778
|
+
const router = new hono.Hono();
|
|
24779
|
+
router.onError(errorHandler);
|
|
24780
|
+
router.use("/*", createRequireAuth({
|
|
24781
|
+
serviceKey
|
|
24782
|
+
}));
|
|
24783
|
+
router.use("/*", requireAdmin);
|
|
24784
|
+
router.get("/", async (c) => {
|
|
24785
|
+
const keys2 = await store.listApiKeys();
|
|
24786
|
+
return c.json({
|
|
24787
|
+
keys: keys2
|
|
24788
|
+
});
|
|
24789
|
+
});
|
|
24790
|
+
router.post("/", async (c) => {
|
|
24791
|
+
const body = await c.req.json();
|
|
24792
|
+
const {
|
|
24793
|
+
name: name2,
|
|
24794
|
+
permissions,
|
|
24795
|
+
rate_limit,
|
|
24796
|
+
expires_at
|
|
24797
|
+
} = body;
|
|
24798
|
+
if (!name2 || typeof name2 !== "string" || name2.trim().length === 0) {
|
|
24799
|
+
throw ApiError.badRequest("Name is required", "INVALID_INPUT");
|
|
24800
|
+
}
|
|
24801
|
+
if (!validatePermissions(permissions)) {
|
|
24802
|
+
throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
|
|
24803
|
+
}
|
|
24804
|
+
if (rate_limit !== void 0 && rate_limit !== null) {
|
|
24805
|
+
if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
|
|
24806
|
+
throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
|
|
24807
|
+
}
|
|
24808
|
+
}
|
|
24809
|
+
if (expires_at !== void 0 && expires_at !== null) {
|
|
24810
|
+
const parsed = new Date(expires_at);
|
|
24811
|
+
if (isNaN(parsed.getTime())) {
|
|
24812
|
+
throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
|
|
24813
|
+
}
|
|
24814
|
+
if (parsed <= /* @__PURE__ */ new Date()) {
|
|
24815
|
+
throw ApiError.badRequest("expires_at must be in the future", "INVALID_INPUT");
|
|
24816
|
+
}
|
|
24817
|
+
}
|
|
24818
|
+
const user = c.get("user");
|
|
24819
|
+
const createdBy = user && typeof user === "object" && "userId" in user ? user.userId : "unknown";
|
|
24820
|
+
const request = {
|
|
24821
|
+
name: name2.trim(),
|
|
24822
|
+
permissions,
|
|
24823
|
+
rate_limit: rate_limit ?? null,
|
|
24824
|
+
expires_at: expires_at ?? null
|
|
24825
|
+
};
|
|
24826
|
+
const keyWithSecret = await store.createApiKey(request, createdBy);
|
|
24827
|
+
return c.json({
|
|
24828
|
+
key: keyWithSecret
|
|
24829
|
+
}, 201);
|
|
24830
|
+
});
|
|
24831
|
+
router.get("/:id", async (c) => {
|
|
24832
|
+
const id = c.req.param("id");
|
|
24833
|
+
const key = await store.getApiKeyById(id);
|
|
24834
|
+
if (!key) {
|
|
24835
|
+
throw ApiError.notFound("API key not found");
|
|
24836
|
+
}
|
|
24837
|
+
return c.json({
|
|
24838
|
+
key
|
|
24839
|
+
});
|
|
24840
|
+
});
|
|
24841
|
+
router.put("/:id", async (c) => {
|
|
24842
|
+
const id = c.req.param("id");
|
|
24843
|
+
const body = await c.req.json();
|
|
24844
|
+
const {
|
|
24845
|
+
name: name2,
|
|
24846
|
+
permissions,
|
|
24847
|
+
rate_limit,
|
|
24848
|
+
expires_at
|
|
24849
|
+
} = body;
|
|
24850
|
+
if (name2 !== void 0) {
|
|
24851
|
+
if (typeof name2 !== "string" || name2.trim().length === 0) {
|
|
24852
|
+
throw ApiError.badRequest("Name must be a non-empty string", "INVALID_INPUT");
|
|
24853
|
+
}
|
|
24854
|
+
}
|
|
24855
|
+
if (permissions !== void 0) {
|
|
24856
|
+
if (!validatePermissions(permissions)) {
|
|
24857
|
+
throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
|
|
24858
|
+
}
|
|
24859
|
+
}
|
|
24860
|
+
if (rate_limit !== void 0 && rate_limit !== null) {
|
|
24861
|
+
if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
|
|
24862
|
+
throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
|
|
24863
|
+
}
|
|
24864
|
+
}
|
|
24865
|
+
if (expires_at !== void 0 && expires_at !== null) {
|
|
24866
|
+
const parsed = new Date(expires_at);
|
|
24867
|
+
if (isNaN(parsed.getTime())) {
|
|
24868
|
+
throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
|
|
24869
|
+
}
|
|
24870
|
+
}
|
|
24871
|
+
const updates = {};
|
|
24872
|
+
if (name2 !== void 0) updates.name = name2.trim();
|
|
24873
|
+
if (permissions !== void 0) updates.permissions = permissions;
|
|
24874
|
+
if (rate_limit !== void 0) updates.rate_limit = rate_limit;
|
|
24875
|
+
if (expires_at !== void 0) updates.expires_at = expires_at;
|
|
24876
|
+
const key = await store.updateApiKey(id, updates);
|
|
24877
|
+
if (!key) {
|
|
24878
|
+
throw ApiError.notFound("API key not found");
|
|
24879
|
+
}
|
|
24880
|
+
return c.json({
|
|
24881
|
+
key
|
|
24882
|
+
});
|
|
24883
|
+
});
|
|
24884
|
+
router.delete("/:id", async (c) => {
|
|
24885
|
+
const id = c.req.param("id");
|
|
24886
|
+
const revoked = await store.revokeApiKey(id);
|
|
24887
|
+
if (!revoked) {
|
|
24888
|
+
throw ApiError.notFound("API key not found or already revoked");
|
|
24889
|
+
}
|
|
24890
|
+
return c.json({
|
|
24891
|
+
success: true
|
|
24892
|
+
});
|
|
24893
|
+
});
|
|
24894
|
+
return router;
|
|
24895
|
+
}
|
|
23445
24896
|
function createCustomAuthAdapter(options2) {
|
|
23446
24897
|
const defaultCapabilities = {
|
|
23447
24898
|
hasBuiltInAuthRoutes: false,
|
|
@@ -23468,7 +24919,6 @@ ${credentialScope}
|
|
|
23468
24919
|
verifyRequest: options2.verifyRequest,
|
|
23469
24920
|
verifyToken: resolvedVerifyToken,
|
|
23470
24921
|
userManagement: options2.userManagement,
|
|
23471
|
-
roleManagement: options2.roleManagement,
|
|
23472
24922
|
getCapabilities() {
|
|
23473
24923
|
return defaultCapabilities;
|
|
23474
24924
|
}
|
|
@@ -23476,9 +24926,13 @@ ${credentialScope}
|
|
|
23476
24926
|
}
|
|
23477
24927
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23478
24928
|
__proto__: null,
|
|
24929
|
+
apiKeyKeyGenerator,
|
|
23479
24930
|
configureJwt,
|
|
23480
24931
|
createAdapterAuthMiddleware,
|
|
23481
24932
|
createAdminRoutes,
|
|
24933
|
+
createApiKeyRateLimiter,
|
|
24934
|
+
createApiKeyRoutes,
|
|
24935
|
+
createApiKeyStore,
|
|
23482
24936
|
createAppleProvider,
|
|
23483
24937
|
createAuthMiddleware,
|
|
23484
24938
|
createAuthRoutes,
|
|
@@ -23504,11 +24958,15 @@ ${credentialScope}
|
|
|
23504
24958
|
getRefreshTokenExpiry,
|
|
23505
24959
|
hashPassword,
|
|
23506
24960
|
hashRefreshToken,
|
|
24961
|
+
httpMethodToOperation,
|
|
24962
|
+
isApiKeyToken,
|
|
24963
|
+
isOperationAllowed,
|
|
23507
24964
|
optionalAuth,
|
|
23508
24965
|
requireAdmin,
|
|
23509
24966
|
requireAuth,
|
|
23510
|
-
|
|
24967
|
+
resolveAuthHooks,
|
|
23511
24968
|
strictAuthLimiter,
|
|
24969
|
+
validateApiKey,
|
|
23512
24970
|
validatePasswordStrength,
|
|
23513
24971
|
verifyAccessToken,
|
|
23514
24972
|
verifyPassword
|
|
@@ -24033,6 +25491,409 @@ ${credentialScope}
|
|
|
24033
25491
|
};
|
|
24034
25492
|
}
|
|
24035
25493
|
}
|
|
25494
|
+
let sharpFactory;
|
|
25495
|
+
async function getSharp() {
|
|
25496
|
+
if (!sharpFactory) {
|
|
25497
|
+
try {
|
|
25498
|
+
const mod = await import("sharp");
|
|
25499
|
+
sharpFactory = mod.default;
|
|
25500
|
+
} catch (err) {
|
|
25501
|
+
throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
|
|
25502
|
+
}
|
|
25503
|
+
}
|
|
25504
|
+
if (!sharpFactory) {
|
|
25505
|
+
throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
|
|
25506
|
+
}
|
|
25507
|
+
return sharpFactory;
|
|
25508
|
+
}
|
|
25509
|
+
const MAX_DIMENSION = 4096;
|
|
25510
|
+
const MAX_QUALITY = 100;
|
|
25511
|
+
const MIN_QUALITY = 1;
|
|
25512
|
+
const VALID_FORMATS = /* @__PURE__ */ new Set(["webp", "avif", "jpeg", "png"]);
|
|
25513
|
+
const VALID_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
|
|
25514
|
+
function parseTransformOptions(query) {
|
|
25515
|
+
const opts = {};
|
|
25516
|
+
let hasTransform = false;
|
|
25517
|
+
if (query.width) {
|
|
25518
|
+
const w2 = parseInt(query.width, 10);
|
|
25519
|
+
if (!Number.isNaN(w2) && w2 > 0) {
|
|
25520
|
+
opts.width = Math.min(w2, MAX_DIMENSION);
|
|
25521
|
+
hasTransform = true;
|
|
25522
|
+
}
|
|
25523
|
+
}
|
|
25524
|
+
if (query.height) {
|
|
25525
|
+
const h2 = parseInt(query.height, 10);
|
|
25526
|
+
if (!Number.isNaN(h2) && h2 > 0) {
|
|
25527
|
+
opts.height = Math.min(h2, MAX_DIMENSION);
|
|
25528
|
+
hasTransform = true;
|
|
25529
|
+
}
|
|
25530
|
+
}
|
|
25531
|
+
if (query.quality) {
|
|
25532
|
+
const q = parseInt(query.quality, 10);
|
|
25533
|
+
if (!Number.isNaN(q)) {
|
|
25534
|
+
opts.quality = Math.min(Math.max(q, MIN_QUALITY), MAX_QUALITY);
|
|
25535
|
+
hasTransform = true;
|
|
25536
|
+
}
|
|
25537
|
+
}
|
|
25538
|
+
if (query.format && VALID_FORMATS.has(query.format)) {
|
|
25539
|
+
opts.format = query.format;
|
|
25540
|
+
hasTransform = true;
|
|
25541
|
+
}
|
|
25542
|
+
if (query.fit && VALID_FITS.has(query.fit)) {
|
|
25543
|
+
opts.fit = query.fit;
|
|
25544
|
+
hasTransform = true;
|
|
25545
|
+
}
|
|
25546
|
+
return hasTransform ? opts : null;
|
|
25547
|
+
}
|
|
25548
|
+
const FORMAT_CONTENT_TYPES = {
|
|
25549
|
+
webp: "image/webp",
|
|
25550
|
+
avif: "image/avif",
|
|
25551
|
+
jpeg: "image/jpeg",
|
|
25552
|
+
png: "image/png"
|
|
25553
|
+
};
|
|
25554
|
+
function isTransformableImage(contentType) {
|
|
25555
|
+
return contentType.startsWith("image/") && !contentType.includes("svg") && !contentType.includes("gif");
|
|
25556
|
+
}
|
|
25557
|
+
async function transformImage(buffer, options2) {
|
|
25558
|
+
const sharp = await getSharp();
|
|
25559
|
+
let pipeline = sharp(buffer);
|
|
25560
|
+
if (options2.width || options2.height) {
|
|
25561
|
+
pipeline = pipeline.resize({
|
|
25562
|
+
width: options2.width,
|
|
25563
|
+
height: options2.height,
|
|
25564
|
+
fit: options2.fit || "cover",
|
|
25565
|
+
withoutEnlargement: true
|
|
25566
|
+
});
|
|
25567
|
+
}
|
|
25568
|
+
const format = options2.format || "webp";
|
|
25569
|
+
const quality = options2.quality || 80;
|
|
25570
|
+
switch (format) {
|
|
25571
|
+
case "webp":
|
|
25572
|
+
pipeline = pipeline.webp({
|
|
25573
|
+
quality
|
|
25574
|
+
});
|
|
25575
|
+
break;
|
|
25576
|
+
case "avif":
|
|
25577
|
+
pipeline = pipeline.avif({
|
|
25578
|
+
quality
|
|
25579
|
+
});
|
|
25580
|
+
break;
|
|
25581
|
+
case "jpeg":
|
|
25582
|
+
pipeline = pipeline.jpeg({
|
|
25583
|
+
quality
|
|
25584
|
+
});
|
|
25585
|
+
break;
|
|
25586
|
+
case "png":
|
|
25587
|
+
pipeline = pipeline.png({
|
|
25588
|
+
quality
|
|
25589
|
+
});
|
|
25590
|
+
break;
|
|
25591
|
+
}
|
|
25592
|
+
const data = await pipeline.toBuffer();
|
|
25593
|
+
return {
|
|
25594
|
+
data,
|
|
25595
|
+
contentType: FORMAT_CONTENT_TYPES[format]
|
|
25596
|
+
};
|
|
25597
|
+
}
|
|
25598
|
+
class TransformCache {
|
|
25599
|
+
cache = /* @__PURE__ */ new Map();
|
|
25600
|
+
maxEntries;
|
|
25601
|
+
maxAgeMs;
|
|
25602
|
+
constructor(maxEntries = 500, maxAgeMs = 36e5) {
|
|
25603
|
+
this.maxEntries = maxEntries;
|
|
25604
|
+
this.maxAgeMs = maxAgeMs;
|
|
25605
|
+
}
|
|
25606
|
+
/** Build a deterministic cache key from file key + transform options. */
|
|
25607
|
+
buildKey(fileKey, options2) {
|
|
25608
|
+
return `${fileKey}::${JSON.stringify(options2)}`;
|
|
25609
|
+
}
|
|
25610
|
+
get(cacheKey) {
|
|
25611
|
+
const entry = this.cache.get(cacheKey);
|
|
25612
|
+
if (!entry) return null;
|
|
25613
|
+
if (Date.now() - entry.timestamp > this.maxAgeMs) {
|
|
25614
|
+
this.cache.delete(cacheKey);
|
|
25615
|
+
return null;
|
|
25616
|
+
}
|
|
25617
|
+
this.cache.delete(cacheKey);
|
|
25618
|
+
this.cache.set(cacheKey, entry);
|
|
25619
|
+
return {
|
|
25620
|
+
data: entry.data,
|
|
25621
|
+
contentType: entry.contentType
|
|
25622
|
+
};
|
|
25623
|
+
}
|
|
25624
|
+
set(cacheKey, data, contentType) {
|
|
25625
|
+
if (this.cache.size >= this.maxEntries) {
|
|
25626
|
+
const oldest = this.cache.keys().next().value;
|
|
25627
|
+
if (oldest !== void 0) this.cache.delete(oldest);
|
|
25628
|
+
}
|
|
25629
|
+
this.cache.set(cacheKey, {
|
|
25630
|
+
data,
|
|
25631
|
+
contentType,
|
|
25632
|
+
timestamp: Date.now()
|
|
25633
|
+
});
|
|
25634
|
+
}
|
|
25635
|
+
}
|
|
25636
|
+
const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
|
|
25637
|
+
const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1e3;
|
|
25638
|
+
class TusHandler {
|
|
25639
|
+
constructor(storageBaseDir, storageController) {
|
|
25640
|
+
this.storageController = storageController;
|
|
25641
|
+
this.tusDir = path$3.join(storageBaseDir, ".tus-uploads");
|
|
25642
|
+
}
|
|
25643
|
+
uploads = /* @__PURE__ */ new Map();
|
|
25644
|
+
tusDir;
|
|
25645
|
+
cleanupTimer;
|
|
25646
|
+
/** Ensure the temp directory exists. */
|
|
25647
|
+
async ensureDir() {
|
|
25648
|
+
if (!fs$4.existsSync(this.tusDir)) {
|
|
25649
|
+
await promises.mkdir(this.tusDir, {
|
|
25650
|
+
recursive: true
|
|
25651
|
+
});
|
|
25652
|
+
}
|
|
25653
|
+
}
|
|
25654
|
+
/** Start periodic cleanup of stale uploads. */
|
|
25655
|
+
startCleanup() {
|
|
25656
|
+
if (this.cleanupTimer) return;
|
|
25657
|
+
this.cleanupTimer = setInterval(() => {
|
|
25658
|
+
void this.cleanupStale();
|
|
25659
|
+
}, 6e4);
|
|
25660
|
+
}
|
|
25661
|
+
/** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
|
|
25662
|
+
async cleanupStale() {
|
|
25663
|
+
const now = Date.now();
|
|
25664
|
+
for (const [id, upload] of this.uploads) {
|
|
25665
|
+
if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
|
|
25666
|
+
try {
|
|
25667
|
+
await promises.unlink(upload.filePath);
|
|
25668
|
+
} catch {
|
|
25669
|
+
}
|
|
25670
|
+
this.uploads.delete(id);
|
|
25671
|
+
}
|
|
25672
|
+
}
|
|
25673
|
+
}
|
|
25674
|
+
// -----------------------------------------------------------------------
|
|
25675
|
+
// TUS Metadata Parsing
|
|
25676
|
+
// -----------------------------------------------------------------------
|
|
25677
|
+
/**
|
|
25678
|
+
* Parse the `Upload-Metadata` header.
|
|
25679
|
+
*
|
|
25680
|
+
* Format: `key base64value,key2 base64value2`
|
|
25681
|
+
*/
|
|
25682
|
+
parseMetadata(header) {
|
|
25683
|
+
const metadata = {};
|
|
25684
|
+
if (!header) return metadata;
|
|
25685
|
+
for (const pair of header.split(",")) {
|
|
25686
|
+
const trimmed = pair.trim();
|
|
25687
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
25688
|
+
if (spaceIdx === -1) {
|
|
25689
|
+
metadata[trimmed] = "";
|
|
25690
|
+
} else {
|
|
25691
|
+
const key = trimmed.substring(0, spaceIdx);
|
|
25692
|
+
const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
|
|
25693
|
+
metadata[key] = value;
|
|
25694
|
+
}
|
|
25695
|
+
}
|
|
25696
|
+
return metadata;
|
|
25697
|
+
}
|
|
25698
|
+
// -----------------------------------------------------------------------
|
|
25699
|
+
// Protocol Endpoints
|
|
25700
|
+
// -----------------------------------------------------------------------
|
|
25701
|
+
/** `OPTIONS /tus` — TUS capability discovery. */
|
|
25702
|
+
options() {
|
|
25703
|
+
return new Response(null, {
|
|
25704
|
+
status: 204,
|
|
25705
|
+
headers: {
|
|
25706
|
+
"Tus-Resumable": "1.0.0",
|
|
25707
|
+
"Tus-Version": "1.0.0",
|
|
25708
|
+
"Tus-Extension": "creation,termination",
|
|
25709
|
+
"Tus-Max-Size": String(MAX_UPLOAD_SIZE)
|
|
25710
|
+
}
|
|
25711
|
+
});
|
|
25712
|
+
}
|
|
25713
|
+
/** `POST /tus` — Create a new upload. */
|
|
25714
|
+
async create(c) {
|
|
25715
|
+
await this.ensureDir();
|
|
25716
|
+
const uploadLengthHeader = c.req.header("Upload-Length");
|
|
25717
|
+
if (!uploadLengthHeader) {
|
|
25718
|
+
return c.json({
|
|
25719
|
+
error: "Upload-Length header is required"
|
|
25720
|
+
}, 400);
|
|
25721
|
+
}
|
|
25722
|
+
const uploadLength = parseInt(uploadLengthHeader, 10);
|
|
25723
|
+
if (Number.isNaN(uploadLength) || uploadLength <= 0) {
|
|
25724
|
+
return c.json({
|
|
25725
|
+
error: "Invalid Upload-Length"
|
|
25726
|
+
}, 400);
|
|
25727
|
+
}
|
|
25728
|
+
if (uploadLength > MAX_UPLOAD_SIZE) {
|
|
25729
|
+
return c.json({
|
|
25730
|
+
error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`
|
|
25731
|
+
}, 413);
|
|
25732
|
+
}
|
|
25733
|
+
const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
|
|
25734
|
+
const id = require$$0$5.randomUUID();
|
|
25735
|
+
const filePath = path$3.join(this.tusDir, id);
|
|
25736
|
+
await promises.writeFile(filePath, Buffer.alloc(0));
|
|
25737
|
+
const upload = {
|
|
25738
|
+
id,
|
|
25739
|
+
size: uploadLength,
|
|
25740
|
+
offset: 0,
|
|
25741
|
+
metadata,
|
|
25742
|
+
createdAt: Date.now(),
|
|
25743
|
+
filePath,
|
|
25744
|
+
bucket: metadata.bucket || void 0,
|
|
25745
|
+
key: metadata.key || metadata.filename || void 0,
|
|
25746
|
+
completed: false
|
|
25747
|
+
};
|
|
25748
|
+
this.uploads.set(id, upload);
|
|
25749
|
+
const reqUrl = new URL(c.req.url);
|
|
25750
|
+
const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
|
|
25751
|
+
return new Response(null, {
|
|
25752
|
+
status: 201,
|
|
25753
|
+
headers: {
|
|
25754
|
+
Location: location,
|
|
25755
|
+
"Tus-Resumable": "1.0.0",
|
|
25756
|
+
"Upload-Offset": "0"
|
|
25757
|
+
}
|
|
25758
|
+
});
|
|
25759
|
+
}
|
|
25760
|
+
/** `HEAD /tus/:id` — Query upload progress. */
|
|
25761
|
+
head(c, id) {
|
|
25762
|
+
const upload = this.uploads.get(id);
|
|
25763
|
+
if (!upload) {
|
|
25764
|
+
return c.json({
|
|
25765
|
+
error: "Upload not found"
|
|
25766
|
+
}, 404);
|
|
25767
|
+
}
|
|
25768
|
+
return new Response(null, {
|
|
25769
|
+
status: 200,
|
|
25770
|
+
headers: {
|
|
25771
|
+
"Tus-Resumable": "1.0.0",
|
|
25772
|
+
"Upload-Offset": String(upload.offset),
|
|
25773
|
+
"Upload-Length": String(upload.size),
|
|
25774
|
+
"Cache-Control": "no-store"
|
|
25775
|
+
}
|
|
25776
|
+
});
|
|
25777
|
+
}
|
|
25778
|
+
/** `PATCH /tus/:id` — Append data to an upload. */
|
|
25779
|
+
async patch(c, id) {
|
|
25780
|
+
const upload = this.uploads.get(id);
|
|
25781
|
+
if (!upload) {
|
|
25782
|
+
return c.json({
|
|
25783
|
+
error: "Upload not found"
|
|
25784
|
+
}, 404);
|
|
25785
|
+
}
|
|
25786
|
+
if (upload.completed) {
|
|
25787
|
+
return c.json({
|
|
25788
|
+
error: "Upload already completed"
|
|
25789
|
+
}, 400);
|
|
25790
|
+
}
|
|
25791
|
+
const offsetHeader = c.req.header("Upload-Offset");
|
|
25792
|
+
if (!offsetHeader) {
|
|
25793
|
+
return c.json({
|
|
25794
|
+
error: "Upload-Offset header is required"
|
|
25795
|
+
}, 400);
|
|
25796
|
+
}
|
|
25797
|
+
const offset = parseInt(offsetHeader, 10);
|
|
25798
|
+
if (offset !== upload.offset) {
|
|
25799
|
+
return c.json({
|
|
25800
|
+
error: "Offset mismatch"
|
|
25801
|
+
}, 409);
|
|
25802
|
+
}
|
|
25803
|
+
const contentType = c.req.header("Content-Type");
|
|
25804
|
+
if (contentType !== "application/offset+octet-stream") {
|
|
25805
|
+
return c.json({
|
|
25806
|
+
error: "Content-Type must be application/offset+octet-stream"
|
|
25807
|
+
}, 415);
|
|
25808
|
+
}
|
|
25809
|
+
const body = await c.req.arrayBuffer();
|
|
25810
|
+
const chunk = Buffer.from(body);
|
|
25811
|
+
if (upload.offset + chunk.length > upload.size) {
|
|
25812
|
+
return c.json({
|
|
25813
|
+
error: "Chunk exceeds declared Upload-Length"
|
|
25814
|
+
}, 413);
|
|
25815
|
+
}
|
|
25816
|
+
const fh = await promises.open(upload.filePath, "a");
|
|
25817
|
+
try {
|
|
25818
|
+
await fh.write(chunk);
|
|
25819
|
+
} finally {
|
|
25820
|
+
await fh.close();
|
|
25821
|
+
}
|
|
25822
|
+
upload.offset += chunk.length;
|
|
25823
|
+
if (upload.offset >= upload.size) {
|
|
25824
|
+
await this.finalize(upload);
|
|
25825
|
+
}
|
|
25826
|
+
return new Response(null, {
|
|
25827
|
+
status: 204,
|
|
25828
|
+
headers: {
|
|
25829
|
+
"Tus-Resumable": "1.0.0",
|
|
25830
|
+
"Upload-Offset": String(upload.offset)
|
|
25831
|
+
}
|
|
25832
|
+
});
|
|
25833
|
+
}
|
|
25834
|
+
/** `DELETE /tus/:id` — Cancel and remove an upload. */
|
|
25835
|
+
async delete(c, id) {
|
|
25836
|
+
const upload = this.uploads.get(id);
|
|
25837
|
+
if (!upload) {
|
|
25838
|
+
return c.json({
|
|
25839
|
+
error: "Upload not found"
|
|
25840
|
+
}, 404);
|
|
25841
|
+
}
|
|
25842
|
+
try {
|
|
25843
|
+
await promises.unlink(upload.filePath);
|
|
25844
|
+
} catch {
|
|
25845
|
+
}
|
|
25846
|
+
this.uploads.delete(id);
|
|
25847
|
+
return new Response(null, {
|
|
25848
|
+
status: 204,
|
|
25849
|
+
headers: {
|
|
25850
|
+
"Tus-Resumable": "1.0.0"
|
|
25851
|
+
}
|
|
25852
|
+
});
|
|
25853
|
+
}
|
|
25854
|
+
// -----------------------------------------------------------------------
|
|
25855
|
+
// Finalization
|
|
25856
|
+
// -----------------------------------------------------------------------
|
|
25857
|
+
/**
|
|
25858
|
+
* Move a completed upload into the storage controller.
|
|
25859
|
+
*/
|
|
25860
|
+
async finalize(upload) {
|
|
25861
|
+
upload.completed = true;
|
|
25862
|
+
if (!this.storageController) {
|
|
25863
|
+
logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", {
|
|
25864
|
+
filePath: upload.filePath
|
|
25865
|
+
});
|
|
25866
|
+
return;
|
|
25867
|
+
}
|
|
25868
|
+
try {
|
|
25869
|
+
const {
|
|
25870
|
+
readFile: readFile2
|
|
25871
|
+
} = await import("fs/promises");
|
|
25872
|
+
const data = await readFile2(upload.filePath);
|
|
25873
|
+
const fileName = upload.key || upload.metadata.filename || upload.id;
|
|
25874
|
+
const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
|
|
25875
|
+
const file = new File([data], fileName, {
|
|
25876
|
+
type: mimeType
|
|
25877
|
+
});
|
|
25878
|
+
await this.storageController.putObject({
|
|
25879
|
+
file,
|
|
25880
|
+
key: fileName,
|
|
25881
|
+
bucket: upload.bucket
|
|
25882
|
+
});
|
|
25883
|
+
try {
|
|
25884
|
+
await promises.unlink(upload.filePath);
|
|
25885
|
+
} catch {
|
|
25886
|
+
}
|
|
25887
|
+
this.uploads.delete(upload.id);
|
|
25888
|
+
logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
|
|
25889
|
+
} catch (err) {
|
|
25890
|
+
logger.error(`[TUS] Failed to finalize upload ${upload.id}`, {
|
|
25891
|
+
error: err
|
|
25892
|
+
});
|
|
25893
|
+
}
|
|
25894
|
+
}
|
|
25895
|
+
}
|
|
25896
|
+
const transformCache = new TransformCache();
|
|
24036
25897
|
function extractWildcardPath(c) {
|
|
24037
25898
|
const routePath = c.req.routePath;
|
|
24038
25899
|
const prefix = routePath.replace("/*", "");
|
|
@@ -24096,6 +25957,7 @@ ${credentialScope}
|
|
|
24096
25957
|
throw ApiError.notFound("File not found");
|
|
24097
25958
|
}
|
|
24098
25959
|
const filePath = decodeURIComponent(rawPath);
|
|
25960
|
+
const transformOpts = parseTransformOptions(c.req.query());
|
|
24099
25961
|
if (controller.getType() === "local") {
|
|
24100
25962
|
const localController = controller;
|
|
24101
25963
|
const {
|
|
@@ -24115,8 +25977,19 @@ ${credentialScope}
|
|
|
24115
25977
|
} catch {
|
|
24116
25978
|
}
|
|
24117
25979
|
}
|
|
24118
|
-
c.header("Content-Type", contentType);
|
|
24119
25980
|
const fileContent = fs__namespace.readFileSync(absolutePath);
|
|
25981
|
+
if (transformOpts && isTransformableImage(contentType)) {
|
|
25982
|
+
const cacheKey = transformCache.buildKey(filePath, transformOpts);
|
|
25983
|
+
let cached = transformCache.get(cacheKey);
|
|
25984
|
+
if (!cached) {
|
|
25985
|
+
cached = await transformImage(Buffer.from(fileContent), transformOpts);
|
|
25986
|
+
transformCache.set(cacheKey, cached.data, cached.contentType);
|
|
25987
|
+
}
|
|
25988
|
+
c.header("Content-Type", cached.contentType);
|
|
25989
|
+
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
25990
|
+
return c.body(new Uint8Array(cached.data));
|
|
25991
|
+
}
|
|
25992
|
+
c.header("Content-Type", contentType);
|
|
24120
25993
|
return c.body(new Uint8Array(fileContent));
|
|
24121
25994
|
}
|
|
24122
25995
|
const {
|
|
@@ -24127,7 +26000,20 @@ ${credentialScope}
|
|
|
24127
26000
|
if (!fileObject) {
|
|
24128
26001
|
throw ApiError.notFound("File not found");
|
|
24129
26002
|
}
|
|
24130
|
-
|
|
26003
|
+
const remoteContentType = fileObject.type || "application/octet-stream";
|
|
26004
|
+
if (transformOpts && isTransformableImage(remoteContentType)) {
|
|
26005
|
+
const cacheKey = transformCache.buildKey(filePath, transformOpts);
|
|
26006
|
+
let cached = transformCache.get(cacheKey);
|
|
26007
|
+
if (!cached) {
|
|
26008
|
+
const buf2 = Buffer.from(await fileObject.arrayBuffer());
|
|
26009
|
+
cached = await transformImage(buf2, transformOpts);
|
|
26010
|
+
transformCache.set(cacheKey, cached.data, cached.contentType);
|
|
26011
|
+
}
|
|
26012
|
+
c.header("Content-Type", cached.contentType);
|
|
26013
|
+
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
26014
|
+
return c.body(new Uint8Array(cached.data));
|
|
26015
|
+
}
|
|
26016
|
+
c.header("Content-Type", remoteContentType);
|
|
24131
26017
|
c.header("Cache-Control", "public, max-age=3600, immutable");
|
|
24132
26018
|
const buf = await fileObject.arrayBuffer();
|
|
24133
26019
|
return c.body(new Uint8Array(buf));
|
|
@@ -24223,6 +26109,14 @@ ${credentialScope}
|
|
|
24223
26109
|
message: "Folder created"
|
|
24224
26110
|
}, 201);
|
|
24225
26111
|
});
|
|
26112
|
+
const tusBaseDir = controller.getType() === "local" ? controller.getBasePath() : process.env.STORAGE_PATH || "./uploads";
|
|
26113
|
+
const tusHandler = new TusHandler(tusBaseDir, controller);
|
|
26114
|
+
tusHandler.startCleanup();
|
|
26115
|
+
router.options("/tus", (_c2) => tusHandler.options());
|
|
26116
|
+
router.post("/tus", writeAuthMiddleware, async (c) => tusHandler.create(c));
|
|
26117
|
+
router.get("/tus/:id", readAuthMiddleware, (c) => tusHandler.head(c, c.req.param("id")));
|
|
26118
|
+
router.patch("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.patch(c, c.req.param("id")));
|
|
26119
|
+
router.delete("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.delete(c, c.req.param("id")));
|
|
24226
26120
|
return router;
|
|
24227
26121
|
}
|
|
24228
26122
|
const DEFAULT_STORAGE_ID = "(default)";
|
|
@@ -24446,6 +26340,13 @@ ${credentialScope}
|
|
|
24446
26340
|
} catch (e) {
|
|
24447
26341
|
}
|
|
24448
26342
|
}
|
|
26343
|
+
const getErrorField = (obj, field) => {
|
|
26344
|
+
const err = obj?.error;
|
|
26345
|
+
if (err && typeof err === "object" && err !== null && field in err) {
|
|
26346
|
+
return err[field];
|
|
26347
|
+
}
|
|
26348
|
+
return obj?.[field];
|
|
26349
|
+
};
|
|
24449
26350
|
if (res.status === 401 && onUnauthorizedHandler) {
|
|
24450
26351
|
const retried = await onUnauthorizedHandler();
|
|
24451
26352
|
if (retried) {
|
|
@@ -24479,7 +26380,7 @@ ${credentialScope}
|
|
|
24479
26380
|
const method = init?.method || "GET";
|
|
24480
26381
|
fallbackMessage = `Endpoint not found (${method} ${path2}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;
|
|
24481
26382
|
}
|
|
24482
|
-
throw new RebaseApiError(retryRes.status, retryBody
|
|
26383
|
+
throw new RebaseApiError(retryRes.status, String(getErrorField(retryBody, "message") || fallbackMessage || `Request failed with status ${retryRes.status}`), getErrorField(retryBody, "code"), getErrorField(retryBody, "details"));
|
|
24483
26384
|
}
|
|
24484
26385
|
return retryBody;
|
|
24485
26386
|
}
|
|
@@ -24490,7 +26391,7 @@ ${credentialScope}
|
|
|
24490
26391
|
const method = init?.method || "GET";
|
|
24491
26392
|
fallbackMessage = `Endpoint not found (${method} ${path2}). This usually means the collection is not registered on the backend, or the frontend API URL configuration (e.g. VITE_API_URL) is missing or pointing to the wrong host.`;
|
|
24492
26393
|
}
|
|
24493
|
-
throw new RebaseApiError(res.status, body
|
|
26394
|
+
throw new RebaseApiError(res.status, String(getErrorField(body, "message") || fallbackMessage || `Request failed with status ${res.status}`), getErrorField(body, "code"), getErrorField(body, "details"));
|
|
24494
26395
|
}
|
|
24495
26396
|
return body;
|
|
24496
26397
|
}
|
|
@@ -25064,33 +26965,6 @@ ${credentialScope}
|
|
|
25064
26965
|
method: "DELETE"
|
|
25065
26966
|
});
|
|
25066
26967
|
}
|
|
25067
|
-
async function listRoles() {
|
|
25068
|
-
return transport.request(adminPath + "/roles", {
|
|
25069
|
-
method: "GET"
|
|
25070
|
-
});
|
|
25071
|
-
}
|
|
25072
|
-
async function getRole(roleId) {
|
|
25073
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
25074
|
-
method: "GET"
|
|
25075
|
-
});
|
|
25076
|
-
}
|
|
25077
|
-
async function createRole(data) {
|
|
25078
|
-
return transport.request(adminPath + "/roles", {
|
|
25079
|
-
method: "POST",
|
|
25080
|
-
body: JSON.stringify(data)
|
|
25081
|
-
});
|
|
25082
|
-
}
|
|
25083
|
-
async function updateRole(roleId, data) {
|
|
25084
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
25085
|
-
method: "PUT",
|
|
25086
|
-
body: JSON.stringify(data)
|
|
25087
|
-
});
|
|
25088
|
-
}
|
|
25089
|
-
async function deleteRole(roleId) {
|
|
25090
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
25091
|
-
method: "DELETE"
|
|
25092
|
-
});
|
|
25093
|
-
}
|
|
25094
26968
|
async function bootstrap() {
|
|
25095
26969
|
return transport.request(adminPath + "/bootstrap", {
|
|
25096
26970
|
method: "POST"
|
|
@@ -25103,11 +26977,6 @@ ${credentialScope}
|
|
|
25103
26977
|
createUser,
|
|
25104
26978
|
updateUser,
|
|
25105
26979
|
deleteUser,
|
|
25106
|
-
listRoles,
|
|
25107
|
-
getRole,
|
|
25108
|
-
createRole,
|
|
25109
|
-
updateRole,
|
|
25110
|
-
deleteRole,
|
|
25111
26980
|
bootstrap
|
|
25112
26981
|
};
|
|
25113
26982
|
}
|
|
@@ -25746,7 +27615,7 @@ ${credentialScope}
|
|
|
25746
27615
|
const http = require$$0$6;
|
|
25747
27616
|
const https = require$$1;
|
|
25748
27617
|
const urllib$2 = require$$0$2;
|
|
25749
|
-
const zlib = require$$
|
|
27618
|
+
const zlib = require$$3;
|
|
25750
27619
|
const PassThrough$3 = require$$0$4.PassThrough;
|
|
25751
27620
|
const Cookies = cookies;
|
|
25752
27621
|
const packageData$7 = require$$9;
|
|
@@ -25981,9 +27850,9 @@ ${credentialScope}
|
|
|
25981
27850
|
const util2 = require$$5;
|
|
25982
27851
|
const fs2 = fs$4;
|
|
25983
27852
|
const nmfetch2 = fetchExports;
|
|
25984
|
-
const dns2 = require$$
|
|
27853
|
+
const dns2 = require$$4$1;
|
|
25985
27854
|
const net2 = require$$0$7;
|
|
25986
|
-
const os2 = require$$1$
|
|
27855
|
+
const os2 = require$$1$3;
|
|
25987
27856
|
const DNS_TTL = 5 * 60 * 1e3;
|
|
25988
27857
|
let networkInterfaces;
|
|
25989
27858
|
try {
|
|
@@ -32129,7 +33998,7 @@ ${credentialScope}
|
|
|
32129
33998
|
const packageData$6 = require$$9;
|
|
32130
33999
|
const MailMessage = mailMessage;
|
|
32131
34000
|
const net$1 = require$$0$7;
|
|
32132
|
-
const dns = require$$
|
|
34001
|
+
const dns = require$$4$1;
|
|
32133
34002
|
const crypto$3 = require$$0$5;
|
|
32134
34003
|
class Mail extends EventEmitter$5 {
|
|
32135
34004
|
constructor(transporter, options2, defaults) {
|
|
@@ -32565,7 +34434,7 @@ ${credentialScope}
|
|
|
32565
34434
|
const EventEmitter$4 = require$$0$9.EventEmitter;
|
|
32566
34435
|
const net = require$$0$7;
|
|
32567
34436
|
const tls = require$$1$1;
|
|
32568
|
-
const os = require$$1$
|
|
34437
|
+
const os = require$$1$3;
|
|
32569
34438
|
const crypto$2 = require$$0$5;
|
|
32570
34439
|
const DataStream = dataStream;
|
|
32571
34440
|
const PassThrough = require$$0$4.PassThrough;
|
|
@@ -36652,6 +38521,9 @@ ${credentialScope}
|
|
|
36652
38521
|
dir: config.collectionsDir
|
|
36653
38522
|
});
|
|
36654
38523
|
}
|
|
38524
|
+
if (config.auth) {
|
|
38525
|
+
activeCollections = Array.from(new Map([defaultUsersCollection, ...activeCollections].map((c) => [c.slug, c])).values());
|
|
38526
|
+
}
|
|
36655
38527
|
const realtimeServices = {};
|
|
36656
38528
|
const delegates = {};
|
|
36657
38529
|
let bootstrappers = config.bootstrappers || [];
|
|
@@ -36720,8 +38592,7 @@ ${credentialScope}
|
|
|
36720
38592
|
id: authAdapter.id
|
|
36721
38593
|
});
|
|
36722
38594
|
authConfigResult = {
|
|
36723
|
-
userService: authAdapter.userManagement ?? {}
|
|
36724
|
-
roleService: authAdapter.roleManagement ?? {}
|
|
38595
|
+
userService: authAdapter.userManagement ?? {}
|
|
36725
38596
|
};
|
|
36726
38597
|
} else {
|
|
36727
38598
|
const safeAuthConfig = config.auth;
|
|
@@ -36753,7 +38624,7 @@ ${credentialScope}
|
|
|
36753
38624
|
oauthProviders,
|
|
36754
38625
|
serviceKey,
|
|
36755
38626
|
hooks: config.hooks,
|
|
36756
|
-
|
|
38627
|
+
authHooks: safeAuthConfig.hooks
|
|
36757
38628
|
});
|
|
36758
38629
|
}
|
|
36759
38630
|
logger.info("Authentication initialized");
|
|
@@ -36898,7 +38769,7 @@ ${credentialScope}
|
|
|
36898
38769
|
oauthProviders,
|
|
36899
38770
|
serviceKey,
|
|
36900
38771
|
hooks: config.hooks,
|
|
36901
|
-
|
|
38772
|
+
authHooks: safeAuthConfig.hooks
|
|
36902
38773
|
});
|
|
36903
38774
|
}
|
|
36904
38775
|
if (authAdapter.createAuthRoutes) {
|
|
@@ -36920,6 +38791,21 @@ ${credentialScope}
|
|
|
36920
38791
|
}
|
|
36921
38792
|
}
|
|
36922
38793
|
}
|
|
38794
|
+
let apiKeyStore;
|
|
38795
|
+
const apiKeyStoreResult = createApiKeyStore(defaultDriver);
|
|
38796
|
+
if (apiKeyStoreResult) {
|
|
38797
|
+
apiKeyStore = apiKeyStoreResult;
|
|
38798
|
+
await apiKeyStore.ensureTable();
|
|
38799
|
+
logger.info("Service API Keys initialized");
|
|
38800
|
+
const apiKeyRoutes = createApiKeyRoutes({
|
|
38801
|
+
store: apiKeyStore,
|
|
38802
|
+
serviceKey
|
|
38803
|
+
});
|
|
38804
|
+
config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
|
|
38805
|
+
logger.info("API key admin routes mounted", {
|
|
38806
|
+
path: `${basePath}/admin/api-keys`
|
|
38807
|
+
});
|
|
38808
|
+
}
|
|
36923
38809
|
if (config.collectionsDir) {
|
|
36924
38810
|
if (process.env.NODE_ENV !== "production") {
|
|
36925
38811
|
const {
|
|
@@ -36970,15 +38856,20 @@ ${credentialScope}
|
|
|
36970
38856
|
dataRouter.use("/*", createAdapterAuthMiddleware({
|
|
36971
38857
|
adapter: authAdapter,
|
|
36972
38858
|
driver: defaultDriver,
|
|
36973
|
-
requireAuth: dataRequireAuth
|
|
38859
|
+
requireAuth: dataRequireAuth,
|
|
38860
|
+
apiKeyStore
|
|
36974
38861
|
}));
|
|
36975
38862
|
} else {
|
|
36976
38863
|
dataRouter.use("/*", createAuthMiddleware({
|
|
36977
38864
|
driver: defaultDriver,
|
|
36978
38865
|
requireAuth: dataRequireAuth,
|
|
36979
|
-
serviceKey
|
|
38866
|
+
serviceKey,
|
|
38867
|
+
apiKeyStore
|
|
36980
38868
|
}));
|
|
36981
38869
|
}
|
|
38870
|
+
if (apiKeyStore) {
|
|
38871
|
+
dataRouter.use("/*", createApiKeyRateLimiter());
|
|
38872
|
+
}
|
|
36982
38873
|
if (historyConfigResult && historyConfigResult.historyService) {
|
|
36983
38874
|
const historyRoutes = createHistoryRoutes({
|
|
36984
38875
|
historyService: historyConfigResult.historyService,
|
|
@@ -37047,6 +38938,13 @@ ${credentialScope}
|
|
|
37047
38938
|
configured: emailService.isConfigured()
|
|
37048
38939
|
});
|
|
37049
38940
|
}
|
|
38941
|
+
const driverAdmin = defaultBootstrapper.getAdmin?.(defaultDriverResult);
|
|
38942
|
+
if (isSQLAdmin(driverAdmin)) {
|
|
38943
|
+
Object.assign(serverClient, {
|
|
38944
|
+
sql: (query, options2) => driverAdmin.executeSql(query, options2)
|
|
38945
|
+
});
|
|
38946
|
+
logger.info("SQL capability attached to singleton");
|
|
38947
|
+
}
|
|
37050
38948
|
_initRebase(serverClient);
|
|
37051
38949
|
logger.info("Rebase singleton initialized");
|
|
37052
38950
|
if (defaultDriverResult.internals) {
|
|
@@ -37072,13 +38970,15 @@ ${credentialScope}
|
|
|
37072
38970
|
functionsRouter.use("/*", createAdapterAuthMiddleware({
|
|
37073
38971
|
adapter: authAdapter,
|
|
37074
38972
|
driver: defaultDriver,
|
|
37075
|
-
requireAuth: functionsRequireAuth
|
|
38973
|
+
requireAuth: functionsRequireAuth,
|
|
38974
|
+
apiKeyStore
|
|
37076
38975
|
}));
|
|
37077
38976
|
} else {
|
|
37078
38977
|
functionsRouter.use("/*", createAuthMiddleware({
|
|
37079
38978
|
driver: defaultDriver,
|
|
37080
38979
|
requireAuth: functionsRequireAuth,
|
|
37081
|
-
serviceKey
|
|
38980
|
+
serviceKey,
|
|
38981
|
+
apiKeyStore
|
|
37082
38982
|
}));
|
|
37083
38983
|
}
|
|
37084
38984
|
const fnRoutes = createFunctionRoutes2(loadedFunctions);
|
|
@@ -47077,7 +48977,7 @@ spurious results.`);
|
|
|
47077
48977
|
return "undefined";
|
|
47078
48978
|
}
|
|
47079
48979
|
if (typeof obj === "string") {
|
|
47080
|
-
return
|
|
48980
|
+
return JSON.stringify(obj);
|
|
47081
48981
|
}
|
|
47082
48982
|
if (typeof obj === "number" || typeof obj === "boolean") {
|
|
47083
48983
|
return String(obj);
|
|
@@ -47108,7 +49008,7 @@ ${indent2}]`;
|
|
|
47108
49008
|
const kind = init.getKind();
|
|
47109
49009
|
const isCode = kind === tsMorph.SyntaxKind.ArrowFunction || kind === tsMorph.SyntaxKind.FunctionExpression || kind === tsMorph.SyntaxKind.Identifier || kind === tsMorph.SyntaxKind.CallExpression || kind === tsMorph.SyntaxKind.JsxElement;
|
|
47110
49010
|
if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
|
|
47111
|
-
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 :
|
|
49011
|
+
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : JSON.stringify(name2);
|
|
47112
49012
|
preservedProps.push(`${keyStr}: ${init.getText()}`);
|
|
47113
49013
|
}
|
|
47114
49014
|
}
|
|
@@ -47118,7 +49018,7 @@ ${indent2}]`;
|
|
|
47118
49018
|
}
|
|
47119
49019
|
if (keys2.length === 0 && preservedProps.length === 0) return "{}";
|
|
47120
49020
|
const props = keys2.map((key) => {
|
|
47121
|
-
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key :
|
|
49021
|
+
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
47122
49022
|
let childAstNode;
|
|
47123
49023
|
if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
|
|
47124
49024
|
const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
|
|
@@ -47160,7 +49060,7 @@ ${indent2}}`;
|
|
|
47160
49060
|
}
|
|
47161
49061
|
} else {
|
|
47162
49062
|
propsObj.addPropertyAssignment({
|
|
47163
|
-
name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey :
|
|
49063
|
+
name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
|
|
47164
49064
|
initializer: newInitializer
|
|
47165
49065
|
});
|
|
47166
49066
|
}
|
|
@@ -48231,7 +50131,7 @@ export default ${safeId}Collection;
|
|
|
48231
50131
|
const mod = await dynamicImport(fileUrl);
|
|
48232
50132
|
const exported = mod.default;
|
|
48233
50133
|
if (!exported) {
|
|
48234
|
-
|
|
50134
|
+
logger.warn(`[functions] ${file}: no default export. Skipping.`);
|
|
48235
50135
|
continue;
|
|
48236
50136
|
}
|
|
48237
50137
|
if (isHonoLike(exported)) {
|
|
@@ -48240,7 +50140,7 @@ export default ${safeId}Collection;
|
|
|
48240
50140
|
name: name2,
|
|
48241
50141
|
app: exported
|
|
48242
50142
|
});
|
|
48243
|
-
|
|
50143
|
+
logger.info(`⚡ Loaded function route: ${name2}`);
|
|
48244
50144
|
continue;
|
|
48245
50145
|
}
|
|
48246
50146
|
if (typeof exported === "function") {
|
|
@@ -48251,20 +50151,20 @@ export default ${safeId}Collection;
|
|
|
48251
50151
|
name: name2,
|
|
48252
50152
|
app: result
|
|
48253
50153
|
});
|
|
48254
|
-
|
|
50154
|
+
logger.info(`⚡ Loaded function route: ${name2}`);
|
|
48255
50155
|
continue;
|
|
48256
50156
|
}
|
|
48257
50157
|
}
|
|
48258
50158
|
const exportType = typeof exported;
|
|
48259
50159
|
const keys2 = exported && typeof exported === "object" ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ") : "N/A";
|
|
48260
|
-
|
|
50160
|
+
logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
|
|
48261
50161
|
export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
|
|
48262
50162
|
prototype methods: ${keys2}
|
|
48263
50163
|
Hint: ensure the function exports a Hono app created with the same hono version as the server.
|
|
48264
50164
|
The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
|
|
48265
50165
|
} catch (err) {
|
|
48266
50166
|
const message = err instanceof Error ? err.message : String(err);
|
|
48267
|
-
|
|
50167
|
+
logger.error(`[functions] Failed to load ${file}: ${message}`);
|
|
48268
50168
|
}
|
|
48269
50169
|
}
|
|
48270
50170
|
}
|
|
@@ -48313,12 +50213,12 @@ export default ${safeId}Collection;
|
|
|
48313
50213
|
const mod = await dynamicImport(fileUrl);
|
|
48314
50214
|
const exported = mod.default;
|
|
48315
50215
|
if (!exported || typeof exported !== "object") {
|
|
48316
|
-
|
|
50216
|
+
logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
|
|
48317
50217
|
continue;
|
|
48318
50218
|
}
|
|
48319
50219
|
const def = exported;
|
|
48320
50220
|
if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
|
|
48321
|
-
|
|
50221
|
+
logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
|
|
48322
50222
|
continue;
|
|
48323
50223
|
}
|
|
48324
50224
|
const id = path__namespace.basename(file, path__namespace.extname(file));
|
|
@@ -48334,10 +50234,10 @@ export default ${safeId}Collection;
|
|
|
48334
50234
|
id,
|
|
48335
50235
|
definition
|
|
48336
50236
|
});
|
|
48337
|
-
|
|
50237
|
+
logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
|
|
48338
50238
|
} catch (err) {
|
|
48339
50239
|
const message = err instanceof Error ? err.message : String(err);
|
|
48340
|
-
|
|
50240
|
+
logger.error(`[cron] Failed to load ${file}: ${message}`);
|
|
48341
50241
|
}
|
|
48342
50242
|
}
|
|
48343
50243
|
}
|
|
@@ -48492,12 +50392,12 @@ export default ${safeId}Collection;
|
|
|
48492
50392
|
for (const loaded of loadedJobs) {
|
|
48493
50393
|
const validation = validateCronExpression(loaded.definition.schedule);
|
|
48494
50394
|
if (!validation.valid) {
|
|
48495
|
-
|
|
50395
|
+
logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
|
|
48496
50396
|
continue;
|
|
48497
50397
|
}
|
|
48498
50398
|
const existing = this.jobs.get(loaded.id);
|
|
48499
50399
|
if (existing) {
|
|
48500
|
-
|
|
50400
|
+
logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
|
|
48501
50401
|
this.stopJob(loaded.id);
|
|
48502
50402
|
}
|
|
48503
50403
|
const enabled = loaded.definition.enabled !== false;
|
|
@@ -48535,7 +50435,9 @@ export default ${safeId}Collection;
|
|
|
48535
50435
|
}
|
|
48536
50436
|
}
|
|
48537
50437
|
}).catch((err) => {
|
|
48538
|
-
|
|
50438
|
+
logger.warn("[cron] Failed to seed job stats from database", {
|
|
50439
|
+
error: err
|
|
50440
|
+
});
|
|
48539
50441
|
});
|
|
48540
50442
|
}
|
|
48541
50443
|
for (const [id, job] of this.jobs) {
|
|
@@ -48543,7 +50445,7 @@ export default ${safeId}Collection;
|
|
|
48543
50445
|
this.scheduleNext(id);
|
|
48544
50446
|
}
|
|
48545
50447
|
}
|
|
48546
|
-
|
|
50448
|
+
logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
|
|
48547
50449
|
}
|
|
48548
50450
|
/**
|
|
48549
50451
|
* Stop the scheduler and clear all timers.
|
|
@@ -48617,7 +50519,7 @@ export default ${safeId}Collection;
|
|
|
48617
50519
|
const job = this.jobs.get(id);
|
|
48618
50520
|
if (!job) return void 0;
|
|
48619
50521
|
if (job.executing) {
|
|
48620
|
-
|
|
50522
|
+
logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
|
|
48621
50523
|
const logEntry = {
|
|
48622
50524
|
jobId: id,
|
|
48623
50525
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -48661,7 +50563,7 @@ export default ${safeId}Collection;
|
|
|
48661
50563
|
const timer = setTimeout(async () => {
|
|
48662
50564
|
if (!job.enabled || !this.started) return;
|
|
48663
50565
|
if (job.executing) {
|
|
48664
|
-
|
|
50566
|
+
logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
|
|
48665
50567
|
this.scheduleNext(id);
|
|
48666
50568
|
return;
|
|
48667
50569
|
}
|
|
@@ -48675,7 +50577,9 @@ export default ${safeId}Collection;
|
|
|
48675
50577
|
}
|
|
48676
50578
|
job.timerId = timer;
|
|
48677
50579
|
} catch (err) {
|
|
48678
|
-
|
|
50580
|
+
logger.error(`[cron] Failed to schedule "${id}"`, {
|
|
50581
|
+
error: err
|
|
50582
|
+
});
|
|
48679
50583
|
job.state = "error";
|
|
48680
50584
|
job.lastError = err instanceof Error ? err.message : String(err);
|
|
48681
50585
|
}
|
|
@@ -48760,13 +50664,15 @@ export default ${safeId}Collection;
|
|
|
48760
50664
|
}
|
|
48761
50665
|
if (this.store) {
|
|
48762
50666
|
this.store.insertLog(logEntry).catch((persistErr) => {
|
|
48763
|
-
|
|
50667
|
+
logger.error(`[cron] Failed to persist log for "${job.id}"`, {
|
|
50668
|
+
error: persistErr
|
|
50669
|
+
});
|
|
48764
50670
|
});
|
|
48765
50671
|
}
|
|
48766
50672
|
if (success) {
|
|
48767
|
-
|
|
50673
|
+
logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
|
|
48768
50674
|
} else {
|
|
48769
|
-
|
|
50675
|
+
logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
|
|
48770
50676
|
}
|
|
48771
50677
|
return logEntry;
|
|
48772
50678
|
}
|
|
@@ -48884,7 +50790,7 @@ export default ${safeId}Collection;
|
|
|
48884
50790
|
function createCronStore(driver) {
|
|
48885
50791
|
const admin = driver.admin;
|
|
48886
50792
|
if (!isSQLAdmin(admin)) {
|
|
48887
|
-
|
|
50793
|
+
logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
|
|
48888
50794
|
return void 0;
|
|
48889
50795
|
}
|
|
48890
50796
|
const exec = admin.executeSql.bind(admin);
|
|
@@ -48910,10 +50816,12 @@ export default ${safeId}Collection;
|
|
|
48910
50816
|
CREATE INDEX IF NOT EXISTS idx_cron_logs_job
|
|
48911
50817
|
ON ${TABLE}(job_id, started_at DESC)
|
|
48912
50818
|
`);
|
|
48913
|
-
|
|
50819
|
+
logger.info("✅ Cron logs table ready");
|
|
48914
50820
|
} catch (err) {
|
|
48915
|
-
|
|
48916
|
-
|
|
50821
|
+
logger.error("❌ Failed to create cron logs table", {
|
|
50822
|
+
error: err
|
|
50823
|
+
});
|
|
50824
|
+
logger.warn("⚠️ Continuing without cron log persistence.");
|
|
48917
50825
|
}
|
|
48918
50826
|
},
|
|
48919
50827
|
async insertLog(entry) {
|
|
@@ -48936,7 +50844,9 @@ export default ${safeId}Collection;
|
|
|
48936
50844
|
)
|
|
48937
50845
|
`);
|
|
48938
50846
|
} catch (err) {
|
|
48939
|
-
|
|
50847
|
+
logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
|
|
50848
|
+
error: err
|
|
50849
|
+
});
|
|
48940
50850
|
}
|
|
48941
50851
|
},
|
|
48942
50852
|
async fetchLogs(jobId, limit = 50) {
|
|
@@ -48950,7 +50860,9 @@ export default ${safeId}Collection;
|
|
|
48950
50860
|
`);
|
|
48951
50861
|
return rows.map(rowToLogEntry);
|
|
48952
50862
|
} catch (err) {
|
|
48953
|
-
|
|
50863
|
+
logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
|
|
50864
|
+
error: err
|
|
50865
|
+
});
|
|
48954
50866
|
return [];
|
|
48955
50867
|
}
|
|
48956
50868
|
},
|
|
@@ -48974,7 +50886,9 @@ export default ${safeId}Collection;
|
|
|
48974
50886
|
});
|
|
48975
50887
|
}
|
|
48976
50888
|
} catch (err) {
|
|
48977
|
-
|
|
50889
|
+
logger.error("[cron-store] Failed to fetch job stats", {
|
|
50890
|
+
error: err
|
|
50891
|
+
});
|
|
48978
50892
|
}
|
|
48979
50893
|
return stats;
|
|
48980
50894
|
}
|
|
@@ -49005,8 +50919,8 @@ export default ${safeId}Collection;
|
|
|
49005
50919
|
indexFile = "index.html"
|
|
49006
50920
|
} = config;
|
|
49007
50921
|
if (!fs__namespace.existsSync(frontendPath)) {
|
|
49008
|
-
|
|
49009
|
-
|
|
50922
|
+
logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
|
|
50923
|
+
logger.warn(" SPA serving is disabled. Build your frontend first.");
|
|
49010
50924
|
return;
|
|
49011
50925
|
}
|
|
49012
50926
|
app.use("/*", serveStatic.serveStatic({
|
|
@@ -49019,13 +50933,13 @@ export default ${safeId}Collection;
|
|
|
49019
50933
|
}
|
|
49020
50934
|
const indexPath = path__namespace.join(frontendPath, indexFile);
|
|
49021
50935
|
if (!fs__namespace.existsSync(indexPath)) {
|
|
49022
|
-
|
|
50936
|
+
logger.warn(`⚠️ Index file not found: ${indexPath}`);
|
|
49023
50937
|
return next();
|
|
49024
50938
|
}
|
|
49025
50939
|
const html = fs__namespace.readFileSync(indexPath, "utf-8");
|
|
49026
50940
|
return c.html(html);
|
|
49027
50941
|
});
|
|
49028
|
-
|
|
50942
|
+
logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
|
|
49029
50943
|
}
|
|
49030
50944
|
const MAX_PORT_ATTEMPTS = 20;
|
|
49031
50945
|
const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
@@ -49033,6 +50947,19 @@ export default ${safeId}Collection;
|
|
|
49033
50947
|
const host = options2?.host ?? "0.0.0.0";
|
|
49034
50948
|
const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
|
|
49035
50949
|
const portFileDir = options2?.portFileDir;
|
|
50950
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
50951
|
+
if (isProd) {
|
|
50952
|
+
return new Promise((resolve, reject) => {
|
|
50953
|
+
const onError = (err) => {
|
|
50954
|
+
reject(err);
|
|
50955
|
+
};
|
|
50956
|
+
server.once("error", onError);
|
|
50957
|
+
server.listen(startPort, host, () => {
|
|
50958
|
+
server.removeListener("error", onError);
|
|
50959
|
+
resolve(startPort);
|
|
50960
|
+
});
|
|
50961
|
+
});
|
|
50962
|
+
}
|
|
49036
50963
|
let affinityPort = null;
|
|
49037
50964
|
if (portFileDir) {
|
|
49038
50965
|
try {
|
|
@@ -49175,6 +51102,8 @@ export default ${safeId}Collection;
|
|
|
49175
51102
|
DB_POOL_MAX: stringType().default("20").transform(Number),
|
|
49176
51103
|
DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
|
|
49177
51104
|
DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
|
|
51105
|
+
DATABASE_DIRECT_URL: stringType().url().optional(),
|
|
51106
|
+
DATABASE_READ_URL: stringType().url().optional(),
|
|
49178
51107
|
FORCE_LOCAL_STORAGE: optionalBoolString,
|
|
49179
51108
|
STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
|
|
49180
51109
|
STORAGE_PATH: stringType().optional(),
|
|
@@ -49250,8 +51179,11 @@ export default ${safeId}Collection;
|
|
|
49250
51179
|
exports2.RestApiGenerator = RestApiGenerator;
|
|
49251
51180
|
exports2.S3StorageController = S3StorageController;
|
|
49252
51181
|
exports2.SMTPEmailService = SMTPEmailService;
|
|
51182
|
+
exports2.TransformCache = TransformCache;
|
|
51183
|
+
exports2.TusHandler = TusHandler;
|
|
49253
51184
|
exports2._resetRebaseMock = _resetRebaseMock;
|
|
49254
51185
|
exports2._setRebaseMock = _setRebaseMock;
|
|
51186
|
+
exports2.apiKeyKeyGenerator = apiKeyKeyGenerator;
|
|
49255
51187
|
exports2.authJwt = authJwt;
|
|
49256
51188
|
exports2.authRoles = authRoles;
|
|
49257
51189
|
exports2.authUid = authUid;
|
|
@@ -49260,6 +51192,9 @@ export default ${safeId}Collection;
|
|
|
49260
51192
|
exports2.configureLogLevel = configureLogLevel;
|
|
49261
51193
|
exports2.createAdapterAuthMiddleware = createAdapterAuthMiddleware;
|
|
49262
51194
|
exports2.createAdminRoutes = createAdminRoutes;
|
|
51195
|
+
exports2.createApiKeyRateLimiter = createApiKeyRateLimiter;
|
|
51196
|
+
exports2.createApiKeyRoutes = createApiKeyRoutes;
|
|
51197
|
+
exports2.createApiKeyStore = createApiKeyStore;
|
|
49263
51198
|
exports2.createAppleProvider = createAppleProvider;
|
|
49264
51199
|
exports2.createAuthMiddleware = createAuthMiddleware;
|
|
49265
51200
|
exports2.createAuthRoutes = createAuthRoutes;
|
|
@@ -49297,27 +51232,35 @@ export default ${safeId}Collection;
|
|
|
49297
51232
|
exports2.getWelcomeEmailTemplate = getWelcomeEmailTemplate;
|
|
49298
51233
|
exports2.hashPassword = hashPassword;
|
|
49299
51234
|
exports2.hashRefreshToken = hashRefreshToken;
|
|
51235
|
+
exports2.httpMethodToOperation = httpMethodToOperation;
|
|
49300
51236
|
exports2.initializeRebaseBackend = initializeRebaseBackend;
|
|
51237
|
+
exports2.isApiKeyToken = isApiKeyToken;
|
|
49301
51238
|
exports2.isAuthAdapter = isAuthAdapter;
|
|
49302
51239
|
exports2.isDatabaseAdapter = isDatabaseAdapter;
|
|
51240
|
+
exports2.isOperationAllowed = isOperationAllowed;
|
|
51241
|
+
exports2.isTransformableImage = isTransformableImage;
|
|
49303
51242
|
exports2.listenWithPortRetry = listenWithPortRetry;
|
|
49304
51243
|
exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
|
|
49305
51244
|
exports2.loadEnv = loadEnv;
|
|
49306
51245
|
exports2.loadFunctionsFromDirectory = loadFunctionsFromDirectory;
|
|
49307
51246
|
exports2.logger = logger;
|
|
49308
51247
|
exports2.optionalAuth = optionalAuth;
|
|
51248
|
+
exports2.parseTransformOptions = parseTransformOptions;
|
|
49309
51249
|
exports2.rebase = rebase;
|
|
49310
51250
|
exports2.requestLogger = requestLogger;
|
|
49311
51251
|
exports2.requireAdmin = requireAdmin;
|
|
49312
51252
|
exports2.requireAuth = requireAuth;
|
|
49313
51253
|
exports2.resetConsole = resetConsole;
|
|
49314
|
-
exports2.
|
|
51254
|
+
exports2.resolveAuthHooks = resolveAuthHooks;
|
|
49315
51255
|
exports2.serveSPA = serveSPA;
|
|
49316
51256
|
exports2.strictAuthLimiter = strictAuthLimiter;
|
|
51257
|
+
exports2.transformImage = transformImage;
|
|
51258
|
+
exports2.validateApiKey = validateApiKey;
|
|
49317
51259
|
exports2.validateCronExpression = validateCronExpression;
|
|
49318
51260
|
exports2.validatePasswordStrength = validatePasswordStrength;
|
|
49319
51261
|
exports2.verifyAccessToken = verifyAccessToken;
|
|
49320
51262
|
exports2.verifyPassword = verifyPassword;
|
|
51263
|
+
exports2.z = z;
|
|
49321
51264
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
49322
51265
|
});
|
|
49323
51266
|
//# sourceMappingURL=index.umd.js.map
|