@rebasepro/server-core 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/src/collections/default-collections.d.ts +12 -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 +2166 -208
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2155 -193
- 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 +2 -24
- package/dist/types/src/controllers/client.d.ts +0 -3
- package/dist/types/src/controllers/collection_registry.d.ts +1 -1
- package/dist/types/src/controllers/data_driver.d.ts +18 -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 +2 -4
- 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 +2 -2
- package/dist/types/src/types/translations.d.ts +28 -12
- package/dist/types/src/types/user_management_delegate.d.ts +6 -4
- package/dist/types/src/users/roles.d.ts +0 -8
- 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 +39 -14
- 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 +31 -19
- 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 +37 -7
- 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 +74 -7
- 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 -4
- 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/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") {
|
|
@@ -2846,10 +2825,15 @@
|
|
|
2846
2825
|
const issue = error2.code === "42703" ? "column" : "table";
|
|
2847
2826
|
logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
|
|
2848
2827
|
}
|
|
2849
|
-
console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2850
2828
|
const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
|
|
2851
2829
|
const pgErrorCode = causePg?.code || error2.code;
|
|
2852
|
-
const
|
|
2830
|
+
const isDbSchemaMismatch = pgErrorCode === "42703" || pgErrorCode === "42P01";
|
|
2831
|
+
if (isDbSchemaMismatch) {
|
|
2832
|
+
console.warn(`⚠️ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2833
|
+
} else {
|
|
2834
|
+
console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2835
|
+
}
|
|
2836
|
+
const suppressStack = isDbSchemaMismatch || statusCode < 500 && code2 === "BAD_REQUEST";
|
|
2853
2837
|
if (!suppressStack) {
|
|
2854
2838
|
console.error(error2.stack || error2);
|
|
2855
2839
|
}
|
|
@@ -2930,7 +2914,7 @@
|
|
|
2930
2914
|
options2.offset = (page - 1) * limit;
|
|
2931
2915
|
}
|
|
2932
2916
|
options2.where = {};
|
|
2933
|
-
const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString"];
|
|
2917
|
+
const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold"];
|
|
2934
2918
|
for (const [key, rawValue] of Object.entries(query)) {
|
|
2935
2919
|
if (reservedQueryKeys.includes(key)) continue;
|
|
2936
2920
|
const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
|
|
@@ -3002,8 +2986,63 @@
|
|
|
3002
2986
|
const fieldsStr = String(query.fields).trim();
|
|
3003
2987
|
options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
3004
2988
|
}
|
|
2989
|
+
if (query.vector_search && query.vector) {
|
|
2990
|
+
const vectorStr = String(query.vector);
|
|
2991
|
+
let queryVector;
|
|
2992
|
+
try {
|
|
2993
|
+
queryVector = JSON.parse(vectorStr);
|
|
2994
|
+
if (!Array.isArray(queryVector) || !queryVector.every((v) => typeof v === "number")) {
|
|
2995
|
+
throw new Error("Expected array of numbers");
|
|
2996
|
+
}
|
|
2997
|
+
} catch {
|
|
2998
|
+
throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
|
|
2999
|
+
}
|
|
3000
|
+
const distanceParam = query.vector_distance ? String(query.vector_distance) : "cosine";
|
|
3001
|
+
if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
|
|
3002
|
+
throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
|
|
3003
|
+
}
|
|
3004
|
+
const vectorSearch = {
|
|
3005
|
+
property: String(query.vector_search),
|
|
3006
|
+
vector: queryVector,
|
|
3007
|
+
distance: distanceParam
|
|
3008
|
+
};
|
|
3009
|
+
if (query.vector_threshold) {
|
|
3010
|
+
const threshold = parseFloat(String(query.vector_threshold));
|
|
3011
|
+
if (isNaN(threshold)) {
|
|
3012
|
+
throw new Error("Invalid vector_threshold. Expected a number.");
|
|
3013
|
+
}
|
|
3014
|
+
vectorSearch.threshold = threshold;
|
|
3015
|
+
}
|
|
3016
|
+
options2.vectorSearch = vectorSearch;
|
|
3017
|
+
}
|
|
3005
3018
|
return options2;
|
|
3006
3019
|
}
|
|
3020
|
+
function httpMethodToOperation(method) {
|
|
3021
|
+
const upper = method.toUpperCase();
|
|
3022
|
+
switch (upper) {
|
|
3023
|
+
case "GET":
|
|
3024
|
+
case "HEAD":
|
|
3025
|
+
case "OPTIONS":
|
|
3026
|
+
return "read";
|
|
3027
|
+
case "POST":
|
|
3028
|
+
case "PUT":
|
|
3029
|
+
case "PATCH":
|
|
3030
|
+
return "write";
|
|
3031
|
+
case "DELETE":
|
|
3032
|
+
return "delete";
|
|
3033
|
+
default:
|
|
3034
|
+
return "read";
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
function isOperationAllowed(permissions, collection, operation) {
|
|
3038
|
+
for (const perm of permissions) {
|
|
3039
|
+
const collectionMatch = perm.collection === "*" || perm.collection === collection;
|
|
3040
|
+
if (collectionMatch && perm.operations.includes(operation)) {
|
|
3041
|
+
return true;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
return false;
|
|
3045
|
+
}
|
|
3007
3046
|
class RestApiGenerator {
|
|
3008
3047
|
collections;
|
|
3009
3048
|
router;
|
|
@@ -3036,6 +3075,19 @@
|
|
|
3036
3075
|
this.createSubcollectionRoutes();
|
|
3037
3076
|
return this.router;
|
|
3038
3077
|
}
|
|
3078
|
+
/**
|
|
3079
|
+
* Check API key permissions for a collection operation.
|
|
3080
|
+
* Throws 403 if the key doesn't have the required permission.
|
|
3081
|
+
* No-ops if the request is not authenticated via an API key.
|
|
3082
|
+
*/
|
|
3083
|
+
enforceApiKeyPermission(c, collectionSlug) {
|
|
3084
|
+
const apiKey = c.get("apiKey");
|
|
3085
|
+
if (!apiKey) return;
|
|
3086
|
+
const operation = httpMethodToOperation(c.req.method);
|
|
3087
|
+
if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
|
|
3088
|
+
throw ApiError.forbidden(`API key does not have "${operation}" permission for collection "${collectionSlug}"`, "API_KEY_FORBIDDEN");
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3039
3091
|
/**
|
|
3040
3092
|
* Get the typed RestFetchService from a driver if it exposes one (for include support).
|
|
3041
3093
|
*/
|
|
@@ -3049,6 +3101,7 @@
|
|
|
3049
3101
|
const basePath = `/${collection.slug}`;
|
|
3050
3102
|
const resolvedCollection = collection;
|
|
3051
3103
|
this.router.get(`${basePath}/count`, async (c) => {
|
|
3104
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3052
3105
|
const queryDict = c.req.query();
|
|
3053
3106
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3054
3107
|
const searchString = queryDict.searchString;
|
|
@@ -3059,6 +3112,7 @@
|
|
|
3059
3112
|
});
|
|
3060
3113
|
});
|
|
3061
3114
|
this.router.get(basePath, async (c) => {
|
|
3115
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3062
3116
|
const queryDict = c.req.query();
|
|
3063
3117
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3064
3118
|
const searchString = queryDict.searchString;
|
|
@@ -3073,7 +3127,8 @@
|
|
|
3073
3127
|
offset: queryOptions.offset,
|
|
3074
3128
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
3075
3129
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3076
|
-
searchString
|
|
3130
|
+
searchString,
|
|
3131
|
+
vectorSearch: queryOptions.vectorSearch
|
|
3077
3132
|
}, queryOptions.include);
|
|
3078
3133
|
entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
|
|
3079
3134
|
const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
|
|
@@ -3101,6 +3156,7 @@
|
|
|
3101
3156
|
});
|
|
3102
3157
|
});
|
|
3103
3158
|
this.router.get(`${basePath}/:id`, async (c) => {
|
|
3159
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3104
3160
|
const id = c.req.param("id");
|
|
3105
3161
|
const queryDict = c.req.query();
|
|
3106
3162
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3131,6 +3187,7 @@
|
|
|
3131
3187
|
});
|
|
3132
3188
|
this.router.post(basePath, async (c) => {
|
|
3133
3189
|
try {
|
|
3190
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3134
3191
|
const driver = c.get("driver") || this.driver;
|
|
3135
3192
|
const path2 = collection.slug;
|
|
3136
3193
|
const hookCtx = this.buildHookContext(c, "POST");
|
|
@@ -3159,6 +3216,7 @@
|
|
|
3159
3216
|
});
|
|
3160
3217
|
this.router.put(`${basePath}/:id`, async (c) => {
|
|
3161
3218
|
try {
|
|
3219
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3162
3220
|
const id = c.req.param("id");
|
|
3163
3221
|
const driver = c.get("driver") || this.driver;
|
|
3164
3222
|
const hookCtx = this.buildHookContext(c, "PUT");
|
|
@@ -3195,6 +3253,7 @@
|
|
|
3195
3253
|
}
|
|
3196
3254
|
});
|
|
3197
3255
|
this.router.delete(`${basePath}/:id`, async (c) => {
|
|
3256
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3198
3257
|
const id = c.req.param("id");
|
|
3199
3258
|
const driver = c.get("driver") || this.driver;
|
|
3200
3259
|
const hookCtx = this.buildHookContext(c, "DELETE");
|
|
@@ -3266,6 +3325,7 @@
|
|
|
3266
3325
|
const parsed = parseSubPath(rawPath);
|
|
3267
3326
|
if (!parsed) return next();
|
|
3268
3327
|
const driver = c.get("driver") || this.driver;
|
|
3328
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3269
3329
|
if (parsed.entityId === "count") {
|
|
3270
3330
|
const queryDict = c.req.query();
|
|
3271
3331
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3313,6 +3373,7 @@
|
|
|
3313
3373
|
const parsed = parseSubPath(rawPath);
|
|
3314
3374
|
if (!parsed || parsed.entityId) return next();
|
|
3315
3375
|
const driver = c.get("driver") || this.driver;
|
|
3376
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3316
3377
|
const body = await c.req.json().catch(() => ({}));
|
|
3317
3378
|
const entity = await driver.saveEntity({
|
|
3318
3379
|
path: parsed.collectionPath,
|
|
@@ -3328,6 +3389,7 @@
|
|
|
3328
3389
|
const parsed = parseSubPath(rawPath);
|
|
3329
3390
|
if (!parsed || !parsed.entityId) return next();
|
|
3330
3391
|
const driver = c.get("driver") || this.driver;
|
|
3392
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3331
3393
|
const body = await c.req.json().catch(() => ({}));
|
|
3332
3394
|
const entity = await driver.saveEntity({
|
|
3333
3395
|
path: parsed.collectionPath,
|
|
@@ -3344,6 +3406,7 @@
|
|
|
3344
3406
|
const parsed = parseSubPath(rawPath);
|
|
3345
3407
|
if (!parsed || !parsed.entityId) return next();
|
|
3346
3408
|
const driver = c.get("driver") || this.driver;
|
|
3409
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3347
3410
|
const existingEntity = await driver.fetchEntity({
|
|
3348
3411
|
path: parsed.collectionPath,
|
|
3349
3412
|
entityId: parsed.entityId
|
|
@@ -3409,7 +3472,8 @@
|
|
|
3409
3472
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
3410
3473
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3411
3474
|
startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
|
|
3412
|
-
searchString
|
|
3475
|
+
searchString,
|
|
3476
|
+
vectorSearch: queryOptions.vectorSearch
|
|
3413
3477
|
});
|
|
3414
3478
|
return entities.map((entity) => this.flattenEntity(entity));
|
|
3415
3479
|
}
|
|
@@ -4935,7 +4999,7 @@
|
|
|
4935
4999
|
const SemVer$6 = semver$4;
|
|
4936
5000
|
const parse$6 = parse_1;
|
|
4937
5001
|
const { safeRe: re, t } = reExports;
|
|
4938
|
-
const coerce$
|
|
5002
|
+
const coerce$2 = (version2, options2) => {
|
|
4939
5003
|
if (version2 instanceof SemVer$6) {
|
|
4940
5004
|
return version2;
|
|
4941
5005
|
}
|
|
@@ -4970,7 +5034,7 @@
|
|
|
4970
5034
|
const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
4971
5035
|
return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
|
|
4972
5036
|
};
|
|
4973
|
-
var coerce_1 = coerce$
|
|
5037
|
+
var coerce_1 = coerce$2;
|
|
4974
5038
|
const parse$5 = parse_1;
|
|
4975
5039
|
const constants$1 = constants$2;
|
|
4976
5040
|
const SemVer$5 = semver$4;
|
|
@@ -5256,19 +5320,19 @@
|
|
|
5256
5320
|
const replaceCaret = (comp, options2) => {
|
|
5257
5321
|
debug2("caret", comp, options2);
|
|
5258
5322
|
const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
|
|
5259
|
-
const
|
|
5323
|
+
const z2 = options2.includePrerelease ? "-0" : "";
|
|
5260
5324
|
return comp.replace(r, (_, M, m2, p, pr) => {
|
|
5261
5325
|
debug2("caret", comp, _, M, m2, p, pr);
|
|
5262
5326
|
let ret;
|
|
5263
5327
|
if (isX(M)) {
|
|
5264
5328
|
ret = "";
|
|
5265
5329
|
} else if (isX(m2)) {
|
|
5266
|
-
ret = `>=${M}.0.0${
|
|
5330
|
+
ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
|
|
5267
5331
|
} else if (isX(p)) {
|
|
5268
5332
|
if (M === "0") {
|
|
5269
|
-
ret = `>=${M}.${m2}.0${
|
|
5333
|
+
ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
|
|
5270
5334
|
} else {
|
|
5271
|
-
ret = `>=${M}.${m2}.0${
|
|
5335
|
+
ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
|
|
5272
5336
|
}
|
|
5273
5337
|
} else if (pr) {
|
|
5274
5338
|
debug2("replaceCaret pr", pr);
|
|
@@ -5285,9 +5349,9 @@
|
|
|
5285
5349
|
debug2("no pr");
|
|
5286
5350
|
if (M === "0") {
|
|
5287
5351
|
if (m2 === "0") {
|
|
5288
|
-
ret = `>=${M}.${m2}.${p}${
|
|
5352
|
+
ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
|
|
5289
5353
|
} else {
|
|
5290
|
-
ret = `>=${M}.${m2}.${p}${
|
|
5354
|
+
ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
|
|
5291
5355
|
}
|
|
5292
5356
|
} else {
|
|
5293
5357
|
ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
|
|
@@ -5944,7 +6008,7 @@
|
|
|
5944
6008
|
const gte = gte_1;
|
|
5945
6009
|
const lte = lte_1;
|
|
5946
6010
|
const cmp = cmp_1;
|
|
5947
|
-
const coerce = coerce_1;
|
|
6011
|
+
const coerce$1 = coerce_1;
|
|
5948
6012
|
const truncate = truncate_1;
|
|
5949
6013
|
const Comparator = requireComparator();
|
|
5950
6014
|
const Range = requireRange();
|
|
@@ -5983,7 +6047,7 @@
|
|
|
5983
6047
|
gte,
|
|
5984
6048
|
lte,
|
|
5985
6049
|
cmp,
|
|
5986
|
-
coerce,
|
|
6050
|
+
coerce: coerce$1,
|
|
5987
6051
|
truncate,
|
|
5988
6052
|
Comparator,
|
|
5989
6053
|
Range,
|
|
@@ -6864,7 +6928,7 @@
|
|
|
6864
6928
|
NotBeforeError: NotBeforeError_1,
|
|
6865
6929
|
TokenExpiredError: TokenExpiredError_1
|
|
6866
6930
|
};
|
|
6867
|
-
const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
|
|
6931
|
+
const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
|
|
6868
6932
|
let jwtConfig = {
|
|
6869
6933
|
secret: "",
|
|
6870
6934
|
accessExpiresIn: "1h",
|
|
@@ -6883,15 +6947,17 @@
|
|
|
6883
6947
|
...config
|
|
6884
6948
|
};
|
|
6885
6949
|
}
|
|
6886
|
-
function generateAccessToken(userId, roles) {
|
|
6950
|
+
function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
|
|
6887
6951
|
if (!jwtConfig.secret) {
|
|
6888
6952
|
throw new Error("JWT secret not configured. Call configureJwt() first.");
|
|
6889
6953
|
}
|
|
6890
6954
|
const payload = {
|
|
6891
6955
|
userId,
|
|
6892
|
-
roles
|
|
6956
|
+
roles,
|
|
6957
|
+
aal,
|
|
6958
|
+
...customClaims
|
|
6893
6959
|
};
|
|
6894
|
-
return jwt.sign(payload, jwtConfig.secret, {
|
|
6960
|
+
return jwt$1.sign(payload, jwtConfig.secret, {
|
|
6895
6961
|
expiresIn: jwtConfig.accessExpiresIn,
|
|
6896
6962
|
algorithm: "HS256"
|
|
6897
6963
|
});
|
|
@@ -6925,7 +6991,7 @@
|
|
|
6925
6991
|
throw new Error("JWT secret not configured. Call configureJwt() first.");
|
|
6926
6992
|
}
|
|
6927
6993
|
try {
|
|
6928
|
-
const decoded = jwt.verify(token, jwtConfig.secret, {
|
|
6994
|
+
const decoded = jwt$1.verify(token, jwtConfig.secret, {
|
|
6929
6995
|
algorithms: ["HS256"]
|
|
6930
6996
|
});
|
|
6931
6997
|
const id = decoded.userId || decoded.uid || decoded.sub;
|
|
@@ -6933,9 +6999,11 @@
|
|
|
6933
6999
|
console.error("[JWT] Verification failed: missing id in payload", decoded);
|
|
6934
7000
|
return null;
|
|
6935
7001
|
}
|
|
7002
|
+
const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
|
|
6936
7003
|
return {
|
|
6937
7004
|
userId: id,
|
|
6938
|
-
roles: decoded.roles || []
|
|
7005
|
+
roles: decoded.roles || [],
|
|
7006
|
+
aal
|
|
6939
7007
|
};
|
|
6940
7008
|
} catch (error2) {
|
|
6941
7009
|
console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
|
|
@@ -6975,6 +7043,17 @@
|
|
|
6975
7043
|
}
|
|
6976
7044
|
return new Date(Date.now() + ms2);
|
|
6977
7045
|
}
|
|
7046
|
+
const jwt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
7047
|
+
__proto__: null,
|
|
7048
|
+
configureJwt,
|
|
7049
|
+
generateAccessToken,
|
|
7050
|
+
generateRefreshToken,
|
|
7051
|
+
getAccessTokenExpiry,
|
|
7052
|
+
getAccessTokenExpiryMs,
|
|
7053
|
+
getRefreshTokenExpiry,
|
|
7054
|
+
hashRefreshToken,
|
|
7055
|
+
verifyAccessToken
|
|
7056
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
6978
7057
|
function isRLSScopedDriver(driver) {
|
|
6979
7058
|
return "withAuth" in driver && typeof driver.withAuth === "function";
|
|
6980
7059
|
}
|
|
@@ -6997,6 +7076,81 @@
|
|
|
6997
7076
|
return false;
|
|
6998
7077
|
}
|
|
6999
7078
|
}
|
|
7079
|
+
function isApiKeyToken(token) {
|
|
7080
|
+
return token.startsWith("rk_");
|
|
7081
|
+
}
|
|
7082
|
+
function hashToken$2(token) {
|
|
7083
|
+
return require$$0$5.createHash("sha256").update(token).digest("hex");
|
|
7084
|
+
}
|
|
7085
|
+
async function validateApiKey(c, token, options2) {
|
|
7086
|
+
const {
|
|
7087
|
+
store,
|
|
7088
|
+
driver
|
|
7089
|
+
} = options2;
|
|
7090
|
+
const hash = hashToken$2(token);
|
|
7091
|
+
const apiKey = await store.findByKeyHash(hash);
|
|
7092
|
+
if (!apiKey) {
|
|
7093
|
+
return c.json({
|
|
7094
|
+
error: {
|
|
7095
|
+
message: "Invalid API key",
|
|
7096
|
+
code: "UNAUTHORIZED"
|
|
7097
|
+
}
|
|
7098
|
+
}, 401);
|
|
7099
|
+
}
|
|
7100
|
+
if (apiKey.revoked_at) {
|
|
7101
|
+
return c.json({
|
|
7102
|
+
error: {
|
|
7103
|
+
message: "API key has been revoked",
|
|
7104
|
+
code: "UNAUTHORIZED"
|
|
7105
|
+
}
|
|
7106
|
+
}, 401);
|
|
7107
|
+
}
|
|
7108
|
+
if (apiKey.expires_at && new Date(apiKey.expires_at) < /* @__PURE__ */ new Date()) {
|
|
7109
|
+
return c.json({
|
|
7110
|
+
error: {
|
|
7111
|
+
message: "API key has expired",
|
|
7112
|
+
code: "UNAUTHORIZED"
|
|
7113
|
+
}
|
|
7114
|
+
}, 401);
|
|
7115
|
+
}
|
|
7116
|
+
const userId = `api-key:${apiKey.id}`;
|
|
7117
|
+
c.set("user", {
|
|
7118
|
+
userId,
|
|
7119
|
+
roles: []
|
|
7120
|
+
});
|
|
7121
|
+
const masked = {
|
|
7122
|
+
id: apiKey.id,
|
|
7123
|
+
name: apiKey.name,
|
|
7124
|
+
key_prefix: apiKey.key_prefix,
|
|
7125
|
+
permissions: apiKey.permissions,
|
|
7126
|
+
rate_limit: apiKey.rate_limit,
|
|
7127
|
+
created_by: apiKey.created_by,
|
|
7128
|
+
created_at: apiKey.created_at,
|
|
7129
|
+
updated_at: apiKey.updated_at,
|
|
7130
|
+
last_used_at: apiKey.last_used_at,
|
|
7131
|
+
expires_at: apiKey.expires_at,
|
|
7132
|
+
revoked_at: apiKey.revoked_at
|
|
7133
|
+
};
|
|
7134
|
+
c.set("apiKey", masked);
|
|
7135
|
+
try {
|
|
7136
|
+
const scopedDriver = await scopeDataDriver(driver, {
|
|
7137
|
+
uid: userId,
|
|
7138
|
+
roles: ["service"]
|
|
7139
|
+
});
|
|
7140
|
+
c.set("driver", scopedDriver);
|
|
7141
|
+
} catch (error2) {
|
|
7142
|
+
console.error("[AUTH] RLS scoping failed for API key:", error2);
|
|
7143
|
+
return c.json({
|
|
7144
|
+
error: {
|
|
7145
|
+
message: "Internal authentication error",
|
|
7146
|
+
code: "INTERNAL_ERROR"
|
|
7147
|
+
}
|
|
7148
|
+
}, 500);
|
|
7149
|
+
}
|
|
7150
|
+
store.updateLastUsed(apiKey.id).catch(() => {
|
|
7151
|
+
});
|
|
7152
|
+
return true;
|
|
7153
|
+
}
|
|
7000
7154
|
const requireAuth = async (c, next) => {
|
|
7001
7155
|
const authHeader = c.req.header("authorization");
|
|
7002
7156
|
const queryToken = c.req.query("token");
|
|
@@ -7103,7 +7257,8 @@
|
|
|
7103
7257
|
driver,
|
|
7104
7258
|
requireAuth: enforceAuth = true,
|
|
7105
7259
|
validator,
|
|
7106
|
-
serviceKey
|
|
7260
|
+
serviceKey,
|
|
7261
|
+
apiKeyStore
|
|
7107
7262
|
} = options2;
|
|
7108
7263
|
return async (c, next) => {
|
|
7109
7264
|
if (validator) {
|
|
@@ -7178,6 +7333,14 @@
|
|
|
7178
7333
|
}
|
|
7179
7334
|
}, 500);
|
|
7180
7335
|
}
|
|
7336
|
+
} else if (apiKeyStore && isApiKeyToken(token)) {
|
|
7337
|
+
const result = await validateApiKey(c, token, {
|
|
7338
|
+
store: apiKeyStore,
|
|
7339
|
+
driver
|
|
7340
|
+
});
|
|
7341
|
+
if (result !== true) {
|
|
7342
|
+
return result;
|
|
7343
|
+
}
|
|
7181
7344
|
} else {
|
|
7182
7345
|
const payload = extractUserFromToken(token);
|
|
7183
7346
|
if (payload) {
|
|
@@ -7238,9 +7401,22 @@
|
|
|
7238
7401
|
const {
|
|
7239
7402
|
adapter,
|
|
7240
7403
|
driver,
|
|
7241
|
-
requireAuth: enforceAuth = true
|
|
7404
|
+
requireAuth: enforceAuth = true,
|
|
7405
|
+
apiKeyStore
|
|
7242
7406
|
} = options2;
|
|
7243
7407
|
return async (c, next) => {
|
|
7408
|
+
if (apiKeyStore) {
|
|
7409
|
+
const authHeader = c.req.header("authorization") || "";
|
|
7410
|
+
const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
7411
|
+
if (token.startsWith("rk_")) {
|
|
7412
|
+
const result = await validateApiKey(c, token, {
|
|
7413
|
+
store: apiKeyStore,
|
|
7414
|
+
driver
|
|
7415
|
+
});
|
|
7416
|
+
if (result === true) return next();
|
|
7417
|
+
return result;
|
|
7418
|
+
}
|
|
7419
|
+
}
|
|
7244
7420
|
let authenticatedUser = null;
|
|
7245
7421
|
try {
|
|
7246
7422
|
authenticatedUser = await adapter.verifyRequest(c.req.raw);
|
|
@@ -7336,11 +7512,11 @@
|
|
|
7336
7512
|
const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
|
|
7337
7513
|
return require$$0$5.timingSafeEqual(derivedKey, storedKey);
|
|
7338
7514
|
}
|
|
7339
|
-
function
|
|
7515
|
+
function resolveAuthHooks(hooks) {
|
|
7340
7516
|
return {
|
|
7341
|
-
hashPassword:
|
|
7342
|
-
verifyPassword:
|
|
7343
|
-
validatePasswordStrength:
|
|
7517
|
+
hashPassword: hooks?.hashPassword ?? hashPassword,
|
|
7518
|
+
verifyPassword: hooks?.verifyPassword ?? verifyPassword,
|
|
7519
|
+
validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
|
|
7344
7520
|
};
|
|
7345
7521
|
}
|
|
7346
7522
|
function getGreeting(user) {
|
|
@@ -7742,6 +7918,63 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7742
7918
|
limit: 50,
|
|
7743
7919
|
message: "Too many requests to this sensitive endpoint, please try again later."
|
|
7744
7920
|
});
|
|
7921
|
+
function apiKeyKeyGenerator(c) {
|
|
7922
|
+
const apiKey = c.get("apiKey");
|
|
7923
|
+
if (apiKey) {
|
|
7924
|
+
return `api-key:${apiKey.id}`;
|
|
7925
|
+
}
|
|
7926
|
+
return defaultKeyGenerator(c);
|
|
7927
|
+
}
|
|
7928
|
+
function createApiKeyRateLimiter(defaultLimit = 1e3, windowMs = 15 * 60 * 1e3) {
|
|
7929
|
+
const store = /* @__PURE__ */ new Map();
|
|
7930
|
+
const cleanupInterval = setInterval(() => {
|
|
7931
|
+
const now = Date.now();
|
|
7932
|
+
for (const [key, entry] of store.entries()) {
|
|
7933
|
+
entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
|
|
7934
|
+
if (entry.timestamps.length === 0) {
|
|
7935
|
+
store.delete(key);
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
}, windowMs);
|
|
7939
|
+
if (cleanupInterval.unref) {
|
|
7940
|
+
cleanupInterval.unref();
|
|
7941
|
+
}
|
|
7942
|
+
return async (c, next) => {
|
|
7943
|
+
const apiKey = c.get("apiKey");
|
|
7944
|
+
if (!apiKey) {
|
|
7945
|
+
return next();
|
|
7946
|
+
}
|
|
7947
|
+
const limit = apiKey.rate_limit ?? defaultLimit;
|
|
7948
|
+
const key = `api-key:${apiKey.id}`;
|
|
7949
|
+
const now = Date.now();
|
|
7950
|
+
let entry = store.get(key);
|
|
7951
|
+
if (!entry) {
|
|
7952
|
+
entry = {
|
|
7953
|
+
timestamps: []
|
|
7954
|
+
};
|
|
7955
|
+
store.set(key, entry);
|
|
7956
|
+
}
|
|
7957
|
+
entry.timestamps = entry.timestamps.filter((t2) => now - t2 < windowMs);
|
|
7958
|
+
if (entry.timestamps.length >= limit) {
|
|
7959
|
+
const retryAfterMs = entry.timestamps[0] + windowMs - now;
|
|
7960
|
+
const retryAfterSec = Math.ceil(retryAfterMs / 1e3);
|
|
7961
|
+
c.header("Retry-After", String(retryAfterSec));
|
|
7962
|
+
c.header("X-RateLimit-Limit", String(limit));
|
|
7963
|
+
c.header("X-RateLimit-Remaining", "0");
|
|
7964
|
+
c.header("X-RateLimit-Reset", String(Math.ceil((now + retryAfterMs) / 1e3)));
|
|
7965
|
+
return c.json({
|
|
7966
|
+
error: {
|
|
7967
|
+
message: "API key rate limit exceeded, please try again later.",
|
|
7968
|
+
code: "RATE_LIMITED"
|
|
7969
|
+
}
|
|
7970
|
+
}, 429);
|
|
7971
|
+
}
|
|
7972
|
+
entry.timestamps.push(now);
|
|
7973
|
+
c.header("X-RateLimit-Limit", String(limit));
|
|
7974
|
+
c.header("X-RateLimit-Remaining", String(limit - entry.timestamps.length));
|
|
7975
|
+
return next();
|
|
7976
|
+
};
|
|
7977
|
+
}
|
|
7745
7978
|
var util$3;
|
|
7746
7979
|
(function(util2) {
|
|
7747
7980
|
util2.assertEqual = (_) => {
|
|
@@ -7892,6 +8125,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7892
8125
|
"not_multiple_of",
|
|
7893
8126
|
"not_finite"
|
|
7894
8127
|
]);
|
|
8128
|
+
const quotelessJson = (obj) => {
|
|
8129
|
+
const json = JSON.stringify(obj, null, 2);
|
|
8130
|
+
return json.replace(/"([^"]+)":/g, "$1:");
|
|
8131
|
+
};
|
|
7895
8132
|
class ZodError extends Error {
|
|
7896
8133
|
get errors() {
|
|
7897
8134
|
return this.issues;
|
|
@@ -8087,6 +8324,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
8087
8324
|
return { message };
|
|
8088
8325
|
};
|
|
8089
8326
|
let overrideErrorMap = errorMap;
|
|
8327
|
+
function setErrorMap(map2) {
|
|
8328
|
+
overrideErrorMap = map2;
|
|
8329
|
+
}
|
|
8090
8330
|
function getErrorMap() {
|
|
8091
8331
|
return overrideErrorMap;
|
|
8092
8332
|
}
|
|
@@ -8115,6 +8355,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
8115
8355
|
message: errorMessage
|
|
8116
8356
|
};
|
|
8117
8357
|
};
|
|
8358
|
+
const EMPTY_PATH = [];
|
|
8118
8359
|
function addIssueToContext(ctx, issueData) {
|
|
8119
8360
|
const overrideMap = getErrorMap();
|
|
8120
8361
|
const issue = makeIssue({
|
|
@@ -10405,6 +10646,113 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10405
10646
|
...processCreateParams(params)
|
|
10406
10647
|
});
|
|
10407
10648
|
};
|
|
10649
|
+
const getDiscriminator = (type) => {
|
|
10650
|
+
if (type instanceof ZodLazy) {
|
|
10651
|
+
return getDiscriminator(type.schema);
|
|
10652
|
+
} else if (type instanceof ZodEffects) {
|
|
10653
|
+
return getDiscriminator(type.innerType());
|
|
10654
|
+
} else if (type instanceof ZodLiteral) {
|
|
10655
|
+
return [type.value];
|
|
10656
|
+
} else if (type instanceof ZodEnum) {
|
|
10657
|
+
return type.options;
|
|
10658
|
+
} else if (type instanceof ZodNativeEnum) {
|
|
10659
|
+
return util$3.objectValues(type.enum);
|
|
10660
|
+
} else if (type instanceof ZodDefault) {
|
|
10661
|
+
return getDiscriminator(type._def.innerType);
|
|
10662
|
+
} else if (type instanceof ZodUndefined) {
|
|
10663
|
+
return [void 0];
|
|
10664
|
+
} else if (type instanceof ZodNull) {
|
|
10665
|
+
return [null];
|
|
10666
|
+
} else if (type instanceof ZodOptional) {
|
|
10667
|
+
return [void 0, ...getDiscriminator(type.unwrap())];
|
|
10668
|
+
} else if (type instanceof ZodNullable) {
|
|
10669
|
+
return [null, ...getDiscriminator(type.unwrap())];
|
|
10670
|
+
} else if (type instanceof ZodBranded) {
|
|
10671
|
+
return getDiscriminator(type.unwrap());
|
|
10672
|
+
} else if (type instanceof ZodReadonly) {
|
|
10673
|
+
return getDiscriminator(type.unwrap());
|
|
10674
|
+
} else if (type instanceof ZodCatch) {
|
|
10675
|
+
return getDiscriminator(type._def.innerType);
|
|
10676
|
+
} else {
|
|
10677
|
+
return [];
|
|
10678
|
+
}
|
|
10679
|
+
};
|
|
10680
|
+
class ZodDiscriminatedUnion extends ZodType {
|
|
10681
|
+
_parse(input) {
|
|
10682
|
+
const { ctx } = this._processInputParams(input);
|
|
10683
|
+
if (ctx.parsedType !== ZodParsedType.object) {
|
|
10684
|
+
addIssueToContext(ctx, {
|
|
10685
|
+
code: ZodIssueCode.invalid_type,
|
|
10686
|
+
expected: ZodParsedType.object,
|
|
10687
|
+
received: ctx.parsedType
|
|
10688
|
+
});
|
|
10689
|
+
return INVALID;
|
|
10690
|
+
}
|
|
10691
|
+
const discriminator = this.discriminator;
|
|
10692
|
+
const discriminatorValue = ctx.data[discriminator];
|
|
10693
|
+
const option = this.optionsMap.get(discriminatorValue);
|
|
10694
|
+
if (!option) {
|
|
10695
|
+
addIssueToContext(ctx, {
|
|
10696
|
+
code: ZodIssueCode.invalid_union_discriminator,
|
|
10697
|
+
options: Array.from(this.optionsMap.keys()),
|
|
10698
|
+
path: [discriminator]
|
|
10699
|
+
});
|
|
10700
|
+
return INVALID;
|
|
10701
|
+
}
|
|
10702
|
+
if (ctx.common.async) {
|
|
10703
|
+
return option._parseAsync({
|
|
10704
|
+
data: ctx.data,
|
|
10705
|
+
path: ctx.path,
|
|
10706
|
+
parent: ctx
|
|
10707
|
+
});
|
|
10708
|
+
} else {
|
|
10709
|
+
return option._parseSync({
|
|
10710
|
+
data: ctx.data,
|
|
10711
|
+
path: ctx.path,
|
|
10712
|
+
parent: ctx
|
|
10713
|
+
});
|
|
10714
|
+
}
|
|
10715
|
+
}
|
|
10716
|
+
get discriminator() {
|
|
10717
|
+
return this._def.discriminator;
|
|
10718
|
+
}
|
|
10719
|
+
get options() {
|
|
10720
|
+
return this._def.options;
|
|
10721
|
+
}
|
|
10722
|
+
get optionsMap() {
|
|
10723
|
+
return this._def.optionsMap;
|
|
10724
|
+
}
|
|
10725
|
+
/**
|
|
10726
|
+
* The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor.
|
|
10727
|
+
* However, it only allows a union of objects, all of which need to share a discriminator property. This property must
|
|
10728
|
+
* have a different value for each object in the union.
|
|
10729
|
+
* @param discriminator the name of the discriminator property
|
|
10730
|
+
* @param types an array of object schemas
|
|
10731
|
+
* @param params
|
|
10732
|
+
*/
|
|
10733
|
+
static create(discriminator, options2, params) {
|
|
10734
|
+
const optionsMap = /* @__PURE__ */ new Map();
|
|
10735
|
+
for (const type of options2) {
|
|
10736
|
+
const discriminatorValues = getDiscriminator(type.shape[discriminator]);
|
|
10737
|
+
if (!discriminatorValues.length) {
|
|
10738
|
+
throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`);
|
|
10739
|
+
}
|
|
10740
|
+
for (const value of discriminatorValues) {
|
|
10741
|
+
if (optionsMap.has(value)) {
|
|
10742
|
+
throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`);
|
|
10743
|
+
}
|
|
10744
|
+
optionsMap.set(value, type);
|
|
10745
|
+
}
|
|
10746
|
+
}
|
|
10747
|
+
return new ZodDiscriminatedUnion({
|
|
10748
|
+
typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
|
|
10749
|
+
discriminator,
|
|
10750
|
+
options: options2,
|
|
10751
|
+
optionsMap,
|
|
10752
|
+
...processCreateParams(params)
|
|
10753
|
+
});
|
|
10754
|
+
}
|
|
10755
|
+
}
|
|
10408
10756
|
function mergeValues(a2, b) {
|
|
10409
10757
|
const aType = getParsedType(a2);
|
|
10410
10758
|
const bType = getParsedType(b);
|
|
@@ -10563,6 +10911,59 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10563
10911
|
...processCreateParams(params)
|
|
10564
10912
|
});
|
|
10565
10913
|
};
|
|
10914
|
+
class ZodRecord extends ZodType {
|
|
10915
|
+
get keySchema() {
|
|
10916
|
+
return this._def.keyType;
|
|
10917
|
+
}
|
|
10918
|
+
get valueSchema() {
|
|
10919
|
+
return this._def.valueType;
|
|
10920
|
+
}
|
|
10921
|
+
_parse(input) {
|
|
10922
|
+
const { status, ctx } = this._processInputParams(input);
|
|
10923
|
+
if (ctx.parsedType !== ZodParsedType.object) {
|
|
10924
|
+
addIssueToContext(ctx, {
|
|
10925
|
+
code: ZodIssueCode.invalid_type,
|
|
10926
|
+
expected: ZodParsedType.object,
|
|
10927
|
+
received: ctx.parsedType
|
|
10928
|
+
});
|
|
10929
|
+
return INVALID;
|
|
10930
|
+
}
|
|
10931
|
+
const pairs = [];
|
|
10932
|
+
const keyType = this._def.keyType;
|
|
10933
|
+
const valueType = this._def.valueType;
|
|
10934
|
+
for (const key in ctx.data) {
|
|
10935
|
+
pairs.push({
|
|
10936
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)),
|
|
10937
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)),
|
|
10938
|
+
alwaysSet: key in ctx.data
|
|
10939
|
+
});
|
|
10940
|
+
}
|
|
10941
|
+
if (ctx.common.async) {
|
|
10942
|
+
return ParseStatus.mergeObjectAsync(status, pairs);
|
|
10943
|
+
} else {
|
|
10944
|
+
return ParseStatus.mergeObjectSync(status, pairs);
|
|
10945
|
+
}
|
|
10946
|
+
}
|
|
10947
|
+
get element() {
|
|
10948
|
+
return this._def.valueType;
|
|
10949
|
+
}
|
|
10950
|
+
static create(first, second, third) {
|
|
10951
|
+
if (second instanceof ZodType) {
|
|
10952
|
+
return new ZodRecord({
|
|
10953
|
+
keyType: first,
|
|
10954
|
+
valueType: second,
|
|
10955
|
+
typeName: ZodFirstPartyTypeKind.ZodRecord,
|
|
10956
|
+
...processCreateParams(third)
|
|
10957
|
+
});
|
|
10958
|
+
}
|
|
10959
|
+
return new ZodRecord({
|
|
10960
|
+
keyType: ZodString.create(),
|
|
10961
|
+
valueType: first,
|
|
10962
|
+
typeName: ZodFirstPartyTypeKind.ZodRecord,
|
|
10963
|
+
...processCreateParams(second)
|
|
10964
|
+
});
|
|
10965
|
+
}
|
|
10966
|
+
}
|
|
10566
10967
|
class ZodMap extends ZodType {
|
|
10567
10968
|
get keySchema() {
|
|
10568
10969
|
return this._def.keyType;
|
|
@@ -10714,6 +11115,111 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10714
11115
|
...processCreateParams(params)
|
|
10715
11116
|
});
|
|
10716
11117
|
};
|
|
11118
|
+
class ZodFunction extends ZodType {
|
|
11119
|
+
constructor() {
|
|
11120
|
+
super(...arguments);
|
|
11121
|
+
this.validate = this.implement;
|
|
11122
|
+
}
|
|
11123
|
+
_parse(input) {
|
|
11124
|
+
const { ctx } = this._processInputParams(input);
|
|
11125
|
+
if (ctx.parsedType !== ZodParsedType.function) {
|
|
11126
|
+
addIssueToContext(ctx, {
|
|
11127
|
+
code: ZodIssueCode.invalid_type,
|
|
11128
|
+
expected: ZodParsedType.function,
|
|
11129
|
+
received: ctx.parsedType
|
|
11130
|
+
});
|
|
11131
|
+
return INVALID;
|
|
11132
|
+
}
|
|
11133
|
+
function makeArgsIssue(args, error2) {
|
|
11134
|
+
return makeIssue({
|
|
11135
|
+
data: args,
|
|
11136
|
+
path: ctx.path,
|
|
11137
|
+
errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
|
|
11138
|
+
issueData: {
|
|
11139
|
+
code: ZodIssueCode.invalid_arguments,
|
|
11140
|
+
argumentsError: error2
|
|
11141
|
+
}
|
|
11142
|
+
});
|
|
11143
|
+
}
|
|
11144
|
+
function makeReturnsIssue(returns, error2) {
|
|
11145
|
+
return makeIssue({
|
|
11146
|
+
data: returns,
|
|
11147
|
+
path: ctx.path,
|
|
11148
|
+
errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), errorMap].filter((x) => !!x),
|
|
11149
|
+
issueData: {
|
|
11150
|
+
code: ZodIssueCode.invalid_return_type,
|
|
11151
|
+
returnTypeError: error2
|
|
11152
|
+
}
|
|
11153
|
+
});
|
|
11154
|
+
}
|
|
11155
|
+
const params = { errorMap: ctx.common.contextualErrorMap };
|
|
11156
|
+
const fn = ctx.data;
|
|
11157
|
+
if (this._def.returns instanceof ZodPromise) {
|
|
11158
|
+
const me = this;
|
|
11159
|
+
return OK(async function(...args) {
|
|
11160
|
+
const error2 = new ZodError([]);
|
|
11161
|
+
const parsedArgs = await me._def.args.parseAsync(args, params).catch((e) => {
|
|
11162
|
+
error2.addIssue(makeArgsIssue(args, e));
|
|
11163
|
+
throw error2;
|
|
11164
|
+
});
|
|
11165
|
+
const result = await Reflect.apply(fn, this, parsedArgs);
|
|
11166
|
+
const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e) => {
|
|
11167
|
+
error2.addIssue(makeReturnsIssue(result, e));
|
|
11168
|
+
throw error2;
|
|
11169
|
+
});
|
|
11170
|
+
return parsedReturns;
|
|
11171
|
+
});
|
|
11172
|
+
} else {
|
|
11173
|
+
const me = this;
|
|
11174
|
+
return OK(function(...args) {
|
|
11175
|
+
const parsedArgs = me._def.args.safeParse(args, params);
|
|
11176
|
+
if (!parsedArgs.success) {
|
|
11177
|
+
throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
|
|
11178
|
+
}
|
|
11179
|
+
const result = Reflect.apply(fn, this, parsedArgs.data);
|
|
11180
|
+
const parsedReturns = me._def.returns.safeParse(result, params);
|
|
11181
|
+
if (!parsedReturns.success) {
|
|
11182
|
+
throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
|
|
11183
|
+
}
|
|
11184
|
+
return parsedReturns.data;
|
|
11185
|
+
});
|
|
11186
|
+
}
|
|
11187
|
+
}
|
|
11188
|
+
parameters() {
|
|
11189
|
+
return this._def.args;
|
|
11190
|
+
}
|
|
11191
|
+
returnType() {
|
|
11192
|
+
return this._def.returns;
|
|
11193
|
+
}
|
|
11194
|
+
args(...items) {
|
|
11195
|
+
return new ZodFunction({
|
|
11196
|
+
...this._def,
|
|
11197
|
+
args: ZodTuple.create(items).rest(ZodUnknown.create())
|
|
11198
|
+
});
|
|
11199
|
+
}
|
|
11200
|
+
returns(returnType) {
|
|
11201
|
+
return new ZodFunction({
|
|
11202
|
+
...this._def,
|
|
11203
|
+
returns: returnType
|
|
11204
|
+
});
|
|
11205
|
+
}
|
|
11206
|
+
implement(func) {
|
|
11207
|
+
const validatedFunc = this.parse(func);
|
|
11208
|
+
return validatedFunc;
|
|
11209
|
+
}
|
|
11210
|
+
strictImplement(func) {
|
|
11211
|
+
const validatedFunc = this.parse(func);
|
|
11212
|
+
return validatedFunc;
|
|
11213
|
+
}
|
|
11214
|
+
static create(args, returns, params) {
|
|
11215
|
+
return new ZodFunction({
|
|
11216
|
+
args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()),
|
|
11217
|
+
returns: returns || ZodUnknown.create(),
|
|
11218
|
+
typeName: ZodFirstPartyTypeKind.ZodFunction,
|
|
11219
|
+
...processCreateParams(params)
|
|
11220
|
+
});
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
10717
11223
|
class ZodLazy extends ZodType {
|
|
10718
11224
|
get schema() {
|
|
10719
11225
|
return this._def.getter();
|
|
@@ -11171,6 +11677,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11171
11677
|
...processCreateParams(params)
|
|
11172
11678
|
});
|
|
11173
11679
|
};
|
|
11680
|
+
const BRAND = Symbol("zod_brand");
|
|
11174
11681
|
class ZodBranded extends ZodType {
|
|
11175
11682
|
_parse(input) {
|
|
11176
11683
|
const { ctx } = this._processInputParams(input);
|
|
@@ -11262,6 +11769,36 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11262
11769
|
...processCreateParams(params)
|
|
11263
11770
|
});
|
|
11264
11771
|
};
|
|
11772
|
+
function cleanParams(params, data) {
|
|
11773
|
+
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
11774
|
+
const p2 = typeof p === "string" ? { message: p } : p;
|
|
11775
|
+
return p2;
|
|
11776
|
+
}
|
|
11777
|
+
function custom(check, _params = {}, fatal) {
|
|
11778
|
+
if (check)
|
|
11779
|
+
return ZodAny.create().superRefine((data, ctx) => {
|
|
11780
|
+
const r = check(data);
|
|
11781
|
+
if (r instanceof Promise) {
|
|
11782
|
+
return r.then((r2) => {
|
|
11783
|
+
if (!r2) {
|
|
11784
|
+
const params = cleanParams(_params, data);
|
|
11785
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
11786
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
11787
|
+
}
|
|
11788
|
+
});
|
|
11789
|
+
}
|
|
11790
|
+
if (!r) {
|
|
11791
|
+
const params = cleanParams(_params, data);
|
|
11792
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
11793
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
11794
|
+
}
|
|
11795
|
+
return;
|
|
11796
|
+
});
|
|
11797
|
+
return ZodAny.create();
|
|
11798
|
+
}
|
|
11799
|
+
const late = {
|
|
11800
|
+
object: ZodObject.lazycreate
|
|
11801
|
+
};
|
|
11265
11802
|
var ZodFirstPartyTypeKind;
|
|
11266
11803
|
(function(ZodFirstPartyTypeKind2) {
|
|
11267
11804
|
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
@@ -11301,17 +11838,251 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11301
11838
|
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
11302
11839
|
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
11303
11840
|
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
11841
|
+
const instanceOfType = (cls, params = {
|
|
11842
|
+
message: `Input not instance of ${cls.name}`
|
|
11843
|
+
}) => custom((data) => data instanceof cls, params);
|
|
11304
11844
|
const stringType = ZodString.create;
|
|
11305
|
-
|
|
11306
|
-
|
|
11845
|
+
const numberType = ZodNumber.create;
|
|
11846
|
+
const nanType = ZodNaN.create;
|
|
11847
|
+
const bigIntType = ZodBigInt.create;
|
|
11848
|
+
const booleanType = ZodBoolean.create;
|
|
11849
|
+
const dateType = ZodDate.create;
|
|
11850
|
+
const symbolType = ZodSymbol.create;
|
|
11851
|
+
const undefinedType = ZodUndefined.create;
|
|
11852
|
+
const nullType = ZodNull.create;
|
|
11853
|
+
const anyType = ZodAny.create;
|
|
11854
|
+
const unknownType = ZodUnknown.create;
|
|
11855
|
+
const neverType = ZodNever.create;
|
|
11856
|
+
const voidType = ZodVoid.create;
|
|
11857
|
+
const arrayType = ZodArray.create;
|
|
11307
11858
|
const objectType = ZodObject.create;
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11859
|
+
const strictObjectType = ZodObject.strictCreate;
|
|
11860
|
+
const unionType = ZodUnion.create;
|
|
11861
|
+
const discriminatedUnionType = ZodDiscriminatedUnion.create;
|
|
11862
|
+
const intersectionType = ZodIntersection.create;
|
|
11863
|
+
const tupleType = ZodTuple.create;
|
|
11864
|
+
const recordType = ZodRecord.create;
|
|
11865
|
+
const mapType = ZodMap.create;
|
|
11866
|
+
const setType = ZodSet.create;
|
|
11867
|
+
const functionType = ZodFunction.create;
|
|
11868
|
+
const lazyType = ZodLazy.create;
|
|
11869
|
+
const literalType = ZodLiteral.create;
|
|
11311
11870
|
const enumType = ZodEnum.create;
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11871
|
+
const nativeEnumType = ZodNativeEnum.create;
|
|
11872
|
+
const promiseType = ZodPromise.create;
|
|
11873
|
+
const effectsType = ZodEffects.create;
|
|
11874
|
+
const optionalType = ZodOptional.create;
|
|
11875
|
+
const nullableType = ZodNullable.create;
|
|
11876
|
+
const preprocessType = ZodEffects.createWithPreprocess;
|
|
11877
|
+
const pipelineType = ZodPipeline.create;
|
|
11878
|
+
const ostring = () => stringType().optional();
|
|
11879
|
+
const onumber = () => numberType().optional();
|
|
11880
|
+
const oboolean = () => booleanType().optional();
|
|
11881
|
+
const coerce = {
|
|
11882
|
+
string: (arg) => ZodString.create({ ...arg, coerce: true }),
|
|
11883
|
+
number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
|
|
11884
|
+
boolean: (arg) => ZodBoolean.create({
|
|
11885
|
+
...arg,
|
|
11886
|
+
coerce: true
|
|
11887
|
+
}),
|
|
11888
|
+
bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
|
|
11889
|
+
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
11890
|
+
};
|
|
11891
|
+
const NEVER = INVALID;
|
|
11892
|
+
const z = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
11893
|
+
__proto__: null,
|
|
11894
|
+
BRAND,
|
|
11895
|
+
DIRTY,
|
|
11896
|
+
EMPTY_PATH,
|
|
11897
|
+
INVALID,
|
|
11898
|
+
NEVER,
|
|
11899
|
+
OK,
|
|
11900
|
+
ParseStatus,
|
|
11901
|
+
Schema: ZodType,
|
|
11902
|
+
ZodAny,
|
|
11903
|
+
ZodArray,
|
|
11904
|
+
ZodBigInt,
|
|
11905
|
+
ZodBoolean,
|
|
11906
|
+
ZodBranded,
|
|
11907
|
+
ZodCatch,
|
|
11908
|
+
ZodDate,
|
|
11909
|
+
ZodDefault,
|
|
11910
|
+
ZodDiscriminatedUnion,
|
|
11911
|
+
ZodEffects,
|
|
11912
|
+
ZodEnum,
|
|
11913
|
+
ZodError,
|
|
11914
|
+
get ZodFirstPartyTypeKind() {
|
|
11915
|
+
return ZodFirstPartyTypeKind;
|
|
11916
|
+
},
|
|
11917
|
+
ZodFunction,
|
|
11918
|
+
ZodIntersection,
|
|
11919
|
+
ZodIssueCode,
|
|
11920
|
+
ZodLazy,
|
|
11921
|
+
ZodLiteral,
|
|
11922
|
+
ZodMap,
|
|
11923
|
+
ZodNaN,
|
|
11924
|
+
ZodNativeEnum,
|
|
11925
|
+
ZodNever,
|
|
11926
|
+
ZodNull,
|
|
11927
|
+
ZodNullable,
|
|
11928
|
+
ZodNumber,
|
|
11929
|
+
ZodObject,
|
|
11930
|
+
ZodOptional,
|
|
11931
|
+
ZodParsedType,
|
|
11932
|
+
ZodPipeline,
|
|
11933
|
+
ZodPromise,
|
|
11934
|
+
ZodReadonly,
|
|
11935
|
+
ZodRecord,
|
|
11936
|
+
ZodSchema: ZodType,
|
|
11937
|
+
ZodSet,
|
|
11938
|
+
ZodString,
|
|
11939
|
+
ZodSymbol,
|
|
11940
|
+
ZodTransformer: ZodEffects,
|
|
11941
|
+
ZodTuple,
|
|
11942
|
+
ZodType,
|
|
11943
|
+
ZodUndefined,
|
|
11944
|
+
ZodUnion,
|
|
11945
|
+
ZodUnknown,
|
|
11946
|
+
ZodVoid,
|
|
11947
|
+
addIssueToContext,
|
|
11948
|
+
any: anyType,
|
|
11949
|
+
array: arrayType,
|
|
11950
|
+
bigint: bigIntType,
|
|
11951
|
+
boolean: booleanType,
|
|
11952
|
+
coerce,
|
|
11953
|
+
custom,
|
|
11954
|
+
date: dateType,
|
|
11955
|
+
datetimeRegex,
|
|
11956
|
+
defaultErrorMap: errorMap,
|
|
11957
|
+
discriminatedUnion: discriminatedUnionType,
|
|
11958
|
+
effect: effectsType,
|
|
11959
|
+
enum: enumType,
|
|
11960
|
+
function: functionType,
|
|
11961
|
+
getErrorMap,
|
|
11962
|
+
getParsedType,
|
|
11963
|
+
instanceof: instanceOfType,
|
|
11964
|
+
intersection: intersectionType,
|
|
11965
|
+
isAborted,
|
|
11966
|
+
isAsync,
|
|
11967
|
+
isDirty,
|
|
11968
|
+
isValid,
|
|
11969
|
+
late,
|
|
11970
|
+
lazy: lazyType,
|
|
11971
|
+
literal: literalType,
|
|
11972
|
+
makeIssue,
|
|
11973
|
+
map: mapType,
|
|
11974
|
+
nan: nanType,
|
|
11975
|
+
nativeEnum: nativeEnumType,
|
|
11976
|
+
never: neverType,
|
|
11977
|
+
null: nullType,
|
|
11978
|
+
nullable: nullableType,
|
|
11979
|
+
number: numberType,
|
|
11980
|
+
object: objectType,
|
|
11981
|
+
get objectUtil() {
|
|
11982
|
+
return objectUtil;
|
|
11983
|
+
},
|
|
11984
|
+
oboolean,
|
|
11985
|
+
onumber,
|
|
11986
|
+
optional: optionalType,
|
|
11987
|
+
ostring,
|
|
11988
|
+
pipeline: pipelineType,
|
|
11989
|
+
preprocess: preprocessType,
|
|
11990
|
+
promise: promiseType,
|
|
11991
|
+
quotelessJson,
|
|
11992
|
+
record: recordType,
|
|
11993
|
+
set: setType,
|
|
11994
|
+
setErrorMap,
|
|
11995
|
+
strictObject: strictObjectType,
|
|
11996
|
+
string: stringType,
|
|
11997
|
+
symbol: symbolType,
|
|
11998
|
+
transformer: effectsType,
|
|
11999
|
+
tuple: tupleType,
|
|
12000
|
+
undefined: undefinedType,
|
|
12001
|
+
union: unionType,
|
|
12002
|
+
unknown: unknownType,
|
|
12003
|
+
get util() {
|
|
12004
|
+
return util$3;
|
|
12005
|
+
},
|
|
12006
|
+
void: voidType
|
|
12007
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
12008
|
+
const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
12009
|
+
function base32Encode(buffer) {
|
|
12010
|
+
let bits = 0;
|
|
12011
|
+
let value = 0;
|
|
12012
|
+
let output = "";
|
|
12013
|
+
for (let i2 = 0; i2 < buffer.length; i2++) {
|
|
12014
|
+
value = value << 8 | buffer[i2];
|
|
12015
|
+
bits += 8;
|
|
12016
|
+
while (bits >= 5) {
|
|
12017
|
+
output += BASE32_ALPHABET[value >>> bits - 5 & 31];
|
|
12018
|
+
bits -= 5;
|
|
12019
|
+
}
|
|
12020
|
+
}
|
|
12021
|
+
if (bits > 0) {
|
|
12022
|
+
output += BASE32_ALPHABET[value << 5 - bits & 31];
|
|
12023
|
+
}
|
|
12024
|
+
return output;
|
|
12025
|
+
}
|
|
12026
|
+
function base32Decode(encoded) {
|
|
12027
|
+
const cleanInput = encoded.replace(/=+$/, "").toUpperCase();
|
|
12028
|
+
const bytes = [];
|
|
12029
|
+
let bits = 0;
|
|
12030
|
+
let value = 0;
|
|
12031
|
+
for (let i2 = 0; i2 < cleanInput.length; i2++) {
|
|
12032
|
+
const index2 = BASE32_ALPHABET.indexOf(cleanInput[i2]);
|
|
12033
|
+
if (index2 === -1) continue;
|
|
12034
|
+
value = value << 5 | index2;
|
|
12035
|
+
bits += 5;
|
|
12036
|
+
if (bits >= 8) {
|
|
12037
|
+
bytes.push(value >>> bits - 8 & 255);
|
|
12038
|
+
bits -= 8;
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
return Buffer.from(bytes);
|
|
12042
|
+
}
|
|
12043
|
+
function generateHotp(secret, counter) {
|
|
12044
|
+
const hmac = require$$0$5.createHmac("sha1", secret);
|
|
12045
|
+
const counterBuffer = Buffer.alloc(8);
|
|
12046
|
+
counterBuffer.writeBigInt64BE(counter);
|
|
12047
|
+
hmac.update(counterBuffer);
|
|
12048
|
+
const hash = hmac.digest();
|
|
12049
|
+
const offset = hash[hash.length - 1] & 15;
|
|
12050
|
+
const code2 = ((hash[offset] & 127) << 24 | hash[offset + 1] << 16 | hash[offset + 2] << 8 | hash[offset + 3]) % 1e6;
|
|
12051
|
+
return code2.toString().padStart(6, "0");
|
|
12052
|
+
}
|
|
12053
|
+
function verifyTotp(secret, token, window2 = 1) {
|
|
12054
|
+
const timeStep = 30;
|
|
12055
|
+
const counter = BigInt(Math.floor(Date.now() / 1e3 / timeStep));
|
|
12056
|
+
for (let i2 = -window2; i2 <= window2; i2++) {
|
|
12057
|
+
if (generateHotp(secret, counter + BigInt(i2)) === token) {
|
|
12058
|
+
return true;
|
|
12059
|
+
}
|
|
12060
|
+
}
|
|
12061
|
+
return false;
|
|
12062
|
+
}
|
|
12063
|
+
function generateTotpSecret(issuer, accountName) {
|
|
12064
|
+
const secretBuffer = require$$0$5.randomBytes(20);
|
|
12065
|
+
const secret = base32Encode(secretBuffer);
|
|
12066
|
+
const encodedIssuer = encodeURIComponent(issuer);
|
|
12067
|
+
const encodedAccount = encodeURIComponent(accountName);
|
|
12068
|
+
const uri = `otpauth://totp/${encodedIssuer}:${encodedAccount}?secret=${secret}&issuer=${encodedIssuer}&algorithm=SHA1&digits=6&period=30`;
|
|
12069
|
+
return {
|
|
12070
|
+
secret,
|
|
12071
|
+
uri
|
|
12072
|
+
};
|
|
12073
|
+
}
|
|
12074
|
+
function generateRecoveryCodes(count = 10) {
|
|
12075
|
+
return Array.from({
|
|
12076
|
+
length: count
|
|
12077
|
+
}, () => {
|
|
12078
|
+
const raw = require$$0$5.randomBytes(5).toString("hex").toUpperCase();
|
|
12079
|
+
const parts = raw.match(/.{1,5}/g);
|
|
12080
|
+
return parts ? parts.join("-") : raw;
|
|
12081
|
+
});
|
|
12082
|
+
}
|
|
12083
|
+
function hashRecoveryCode(code2) {
|
|
12084
|
+
return require$$0$5.createHash("sha256").update(code2.replace(/-/g, "").toUpperCase()).digest("hex");
|
|
12085
|
+
}
|
|
11315
12086
|
function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
|
|
11316
12087
|
return {
|
|
11317
12088
|
user: {
|
|
@@ -11349,9 +12120,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11349
12120
|
emailService,
|
|
11350
12121
|
emailConfig,
|
|
11351
12122
|
allowRegistration = false,
|
|
11352
|
-
|
|
12123
|
+
authHooks
|
|
11353
12124
|
} = config;
|
|
11354
|
-
const ops =
|
|
12125
|
+
const ops = resolveAuthHooks(authHooks);
|
|
11355
12126
|
const registerSchema = objectType({
|
|
11356
12127
|
email: stringType().email("Invalid email address").max(255),
|
|
11357
12128
|
password: stringType().min(1, "Password is required").max(128),
|
|
@@ -11415,7 +12186,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11415
12186
|
async function createSessionAndTokens(userId, userAgent, ipAddress) {
|
|
11416
12187
|
const roles = await authRepo.getUserRoles(userId);
|
|
11417
12188
|
const roleIds = roles.map((r) => r.id);
|
|
11418
|
-
|
|
12189
|
+
let customClaims;
|
|
12190
|
+
if (authHooks?.customizeAccessToken) {
|
|
12191
|
+
const user = await authRepo.getUserById(userId);
|
|
12192
|
+
if (user) {
|
|
12193
|
+
const defaultClaims = {
|
|
12194
|
+
userId,
|
|
12195
|
+
roles: roleIds,
|
|
12196
|
+
aal: "aal1"
|
|
12197
|
+
};
|
|
12198
|
+
customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
|
|
12199
|
+
}
|
|
12200
|
+
}
|
|
12201
|
+
const accessToken = generateAccessToken(userId, roleIds, "aal1", customClaims);
|
|
11419
12202
|
const refreshToken = generateRefreshToken();
|
|
11420
12203
|
await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
|
|
11421
12204
|
return {
|
|
@@ -11450,8 +12233,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11450
12233
|
passwordHash,
|
|
11451
12234
|
displayName: displayName || void 0
|
|
11452
12235
|
};
|
|
11453
|
-
if (
|
|
11454
|
-
createData = await
|
|
12236
|
+
if (authHooks?.beforeUserCreate) {
|
|
12237
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
11455
12238
|
}
|
|
11456
12239
|
const user = await authRepo.createUser(createData);
|
|
11457
12240
|
const existingUsers = await authRepo.listUsers();
|
|
@@ -11470,14 +12253,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11470
12253
|
email: user.email,
|
|
11471
12254
|
displayName: user.displayName
|
|
11472
12255
|
});
|
|
11473
|
-
if (
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
})
|
|
12256
|
+
if (authHooks?.afterUserCreate) {
|
|
12257
|
+
try {
|
|
12258
|
+
await authHooks.afterUserCreate(user);
|
|
12259
|
+
} catch (err) {
|
|
12260
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
12261
|
+
}
|
|
11477
12262
|
}
|
|
11478
|
-
if (
|
|
11479
|
-
|
|
11480
|
-
console.error("[
|
|
12263
|
+
if (authHooks?.onAuthenticated) {
|
|
12264
|
+
authHooks.onAuthenticated(user, "register").catch((err) => {
|
|
12265
|
+
console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
|
|
11481
12266
|
});
|
|
11482
12267
|
}
|
|
11483
12268
|
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
|
|
@@ -11487,9 +12272,12 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11487
12272
|
email,
|
|
11488
12273
|
password
|
|
11489
12274
|
} = parseBody2(loginSchema, await c.req.json());
|
|
12275
|
+
if (authHooks?.beforeLogin) {
|
|
12276
|
+
await authHooks.beforeLogin(email, "login");
|
|
12277
|
+
}
|
|
11490
12278
|
let user;
|
|
11491
|
-
if (
|
|
11492
|
-
user = await
|
|
12279
|
+
if (authHooks?.verifyCredentials) {
|
|
12280
|
+
user = await authHooks.verifyCredentials(email, password, authRepo);
|
|
11493
12281
|
if (!user) {
|
|
11494
12282
|
throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
|
|
11495
12283
|
}
|
|
@@ -11511,9 +12299,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11511
12299
|
accessToken,
|
|
11512
12300
|
refreshToken
|
|
11513
12301
|
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
11514
|
-
if (
|
|
11515
|
-
|
|
11516
|
-
console.error("[
|
|
12302
|
+
if (authHooks?.onAuthenticated) {
|
|
12303
|
+
authHooks.onAuthenticated(user, "login").catch((err) => {
|
|
12304
|
+
console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
|
|
11517
12305
|
});
|
|
11518
12306
|
}
|
|
11519
12307
|
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
|
|
@@ -11552,6 +12340,13 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11552
12340
|
await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
|
|
11553
12341
|
email: externalUser.email
|
|
11554
12342
|
});
|
|
12343
|
+
if (authHooks?.afterUserCreate) {
|
|
12344
|
+
try {
|
|
12345
|
+
await authHooks.afterUserCreate(user);
|
|
12346
|
+
} catch (err) {
|
|
12347
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
12348
|
+
}
|
|
12349
|
+
}
|
|
11555
12350
|
const allUsers = await authRepo.listUsers();
|
|
11556
12351
|
const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
|
|
11557
12352
|
if (isFirstUser) {
|
|
@@ -11637,6 +12432,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11637
12432
|
await authRepo.updatePassword(storedToken.userId, passwordHash);
|
|
11638
12433
|
await authRepo.markPasswordResetTokenUsed(tokenHash);
|
|
11639
12434
|
await authRepo.deleteAllRefreshTokensForUser(storedToken.userId);
|
|
12435
|
+
if (authHooks?.onPasswordReset) {
|
|
12436
|
+
authHooks.onPasswordReset(storedToken.userId).catch((err) => {
|
|
12437
|
+
console.error("[AuthHooks] onPasswordReset error:", err instanceof Error ? err.message : err);
|
|
12438
|
+
});
|
|
12439
|
+
}
|
|
11640
12440
|
return c.json({
|
|
11641
12441
|
success: true,
|
|
11642
12442
|
message: "Password has been reset successfully"
|
|
@@ -11740,7 +12540,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11740
12540
|
}
|
|
11741
12541
|
const roles = await authRepo.getUserRoles(storedToken.userId);
|
|
11742
12542
|
const roleIds = roles.map((r) => r.id);
|
|
11743
|
-
|
|
12543
|
+
let customClaims;
|
|
12544
|
+
if (authHooks?.customizeAccessToken) {
|
|
12545
|
+
const user = await authRepo.getUserById(storedToken.userId);
|
|
12546
|
+
if (user) {
|
|
12547
|
+
const defaultClaims = {
|
|
12548
|
+
userId: storedToken.userId,
|
|
12549
|
+
roles: roleIds,
|
|
12550
|
+
aal: "aal1"
|
|
12551
|
+
};
|
|
12552
|
+
customClaims = await authHooks.customizeAccessToken(defaultClaims, user);
|
|
12553
|
+
}
|
|
12554
|
+
}
|
|
12555
|
+
const newAccessToken = generateAccessToken(storedToken.userId, roleIds, "aal1", customClaims);
|
|
11744
12556
|
const newRefreshToken = generateRefreshToken();
|
|
11745
12557
|
const userAgent = c.req.header("user-agent") || "unknown";
|
|
11746
12558
|
const ipAddress = c.req.header("x-forwarded-for") || "unknown";
|
|
@@ -11762,6 +12574,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11762
12574
|
const tokenHash = hashRefreshToken(refreshToken);
|
|
11763
12575
|
await authRepo.deleteRefreshToken(tokenHash);
|
|
11764
12576
|
}
|
|
12577
|
+
const authHeader = c.req.header("authorization");
|
|
12578
|
+
if (authHooks?.afterLogout && authHeader?.startsWith("Bearer ")) {
|
|
12579
|
+
const {
|
|
12580
|
+
verifyAccessToken: verifyAccessToken2
|
|
12581
|
+
} = await Promise.resolve().then(() => jwt);
|
|
12582
|
+
const payload = verifyAccessToken2(authHeader.substring(7));
|
|
12583
|
+
if (payload) {
|
|
12584
|
+
authHooks.afterLogout(payload.userId).catch((err) => {
|
|
12585
|
+
console.error("[AuthHooks] afterLogout error:", err instanceof Error ? err.message : err);
|
|
12586
|
+
});
|
|
12587
|
+
}
|
|
12588
|
+
}
|
|
11765
12589
|
return c.json({
|
|
11766
12590
|
success: true
|
|
11767
12591
|
});
|
|
@@ -11881,6 +12705,270 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11881
12705
|
enabledProviders
|
|
11882
12706
|
});
|
|
11883
12707
|
});
|
|
12708
|
+
router.post("/anonymous", strictAuthLimiter, async (c) => {
|
|
12709
|
+
const anonId = require$$0$5.randomBytes(16).toString("hex");
|
|
12710
|
+
const anonEmail = `anon_${anonId.slice(0, 8)}@anonymous.local`;
|
|
12711
|
+
let createData = {
|
|
12712
|
+
email: anonEmail,
|
|
12713
|
+
emailVerified: false,
|
|
12714
|
+
isAnonymous: true
|
|
12715
|
+
};
|
|
12716
|
+
if (authHooks?.beforeUserCreate) {
|
|
12717
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
12718
|
+
}
|
|
12719
|
+
const user = await authRepo.createUser(createData);
|
|
12720
|
+
if (config.defaultRole) {
|
|
12721
|
+
await authRepo.assignDefaultRole(user.id, config.defaultRole);
|
|
12722
|
+
}
|
|
12723
|
+
const {
|
|
12724
|
+
roleIds,
|
|
12725
|
+
accessToken,
|
|
12726
|
+
refreshToken
|
|
12727
|
+
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
12728
|
+
if (authHooks?.afterUserCreate) {
|
|
12729
|
+
authHooks.afterUserCreate(user).catch((err) => {
|
|
12730
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
12731
|
+
});
|
|
12732
|
+
}
|
|
12733
|
+
if (authHooks?.onAuthenticated) {
|
|
12734
|
+
authHooks.onAuthenticated(user, "anonymous").catch((err) => {
|
|
12735
|
+
console.error("[AuthHooks] onAuthenticated error:", err instanceof Error ? err.message : err);
|
|
12736
|
+
});
|
|
12737
|
+
}
|
|
12738
|
+
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
|
|
12739
|
+
});
|
|
12740
|
+
router.post("/anonymous/link", requireAuth, async (c) => {
|
|
12741
|
+
const userCtx = c.get("user");
|
|
12742
|
+
if (!userCtx) {
|
|
12743
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12744
|
+
}
|
|
12745
|
+
const user = await authRepo.getUserById(userCtx.userId);
|
|
12746
|
+
if (!user?.isAnonymous) {
|
|
12747
|
+
throw ApiError.badRequest("User is not anonymous", "NOT_ANONYMOUS");
|
|
12748
|
+
}
|
|
12749
|
+
const linkSchema = objectType({
|
|
12750
|
+
email: stringType().email("Invalid email address").max(255),
|
|
12751
|
+
password: stringType().min(1, "Password is required").max(128)
|
|
12752
|
+
});
|
|
12753
|
+
const {
|
|
12754
|
+
email,
|
|
12755
|
+
password
|
|
12756
|
+
} = parseBody2(linkSchema, await c.req.json());
|
|
12757
|
+
const passwordValidation = ops.validatePasswordStrength(password);
|
|
12758
|
+
if (!passwordValidation.valid) {
|
|
12759
|
+
throw ApiError.badRequest(passwordValidation.errors.join(". "), "WEAK_PASSWORD");
|
|
12760
|
+
}
|
|
12761
|
+
const existingUser = await authRepo.getUserByEmail(email.toLowerCase());
|
|
12762
|
+
if (existingUser) {
|
|
12763
|
+
throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
|
|
12764
|
+
}
|
|
12765
|
+
const passwordHash = await ops.hashPassword(password);
|
|
12766
|
+
const updatedUser = await authRepo.updateUser(user.id, {
|
|
12767
|
+
email: email.toLowerCase(),
|
|
12768
|
+
passwordHash,
|
|
12769
|
+
isAnonymous: false
|
|
12770
|
+
});
|
|
12771
|
+
if (!updatedUser) {
|
|
12772
|
+
throw ApiError.notFound("User not found");
|
|
12773
|
+
}
|
|
12774
|
+
const {
|
|
12775
|
+
roleIds,
|
|
12776
|
+
accessToken,
|
|
12777
|
+
refreshToken
|
|
12778
|
+
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
12779
|
+
return c.json(buildAuthResponse(updatedUser, roleIds, accessToken, refreshToken));
|
|
12780
|
+
});
|
|
12781
|
+
router.post("/mfa/enroll", requireAuth, async (c) => {
|
|
12782
|
+
const userCtx = c.get("user");
|
|
12783
|
+
if (!userCtx) {
|
|
12784
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12785
|
+
}
|
|
12786
|
+
const body = await c.req.json().catch(() => ({}));
|
|
12787
|
+
const friendlyName = typeof body.friendlyName === "string" ? body.friendlyName : void 0;
|
|
12788
|
+
const issuer = typeof body.issuer === "string" ? body.issuer : emailConfig?.appName || "Rebase";
|
|
12789
|
+
const user = await authRepo.getUserById(userCtx.userId);
|
|
12790
|
+
if (!user) {
|
|
12791
|
+
throw ApiError.notFound("User not found");
|
|
12792
|
+
}
|
|
12793
|
+
const {
|
|
12794
|
+
secret,
|
|
12795
|
+
uri
|
|
12796
|
+
} = generateTotpSecret(issuer, user.email);
|
|
12797
|
+
const factor = await authRepo.createMfaFactor(
|
|
12798
|
+
user.id,
|
|
12799
|
+
"totp",
|
|
12800
|
+
secret,
|
|
12801
|
+
// In production, encrypt this before storage
|
|
12802
|
+
friendlyName
|
|
12803
|
+
);
|
|
12804
|
+
const codes = generateRecoveryCodes(10);
|
|
12805
|
+
const codeHashes = codes.map(hashRecoveryCode);
|
|
12806
|
+
await authRepo.createRecoveryCodes(user.id, codeHashes);
|
|
12807
|
+
return c.json({
|
|
12808
|
+
factor: {
|
|
12809
|
+
id: factor.id,
|
|
12810
|
+
factorType: factor.factorType,
|
|
12811
|
+
friendlyName: factor.friendlyName
|
|
12812
|
+
},
|
|
12813
|
+
totp: {
|
|
12814
|
+
secret,
|
|
12815
|
+
uri,
|
|
12816
|
+
qrUri: uri
|
|
12817
|
+
// Client can use a QR library to render this
|
|
12818
|
+
},
|
|
12819
|
+
recoveryCodes: codes
|
|
12820
|
+
}, 201);
|
|
12821
|
+
});
|
|
12822
|
+
router.post("/mfa/verify", requireAuth, async (c) => {
|
|
12823
|
+
const userCtx = c.get("user");
|
|
12824
|
+
if (!userCtx) {
|
|
12825
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12826
|
+
}
|
|
12827
|
+
const verifySchema = objectType({
|
|
12828
|
+
factorId: stringType().min(1, "Factor ID is required"),
|
|
12829
|
+
code: stringType().length(6, "Code must be 6 digits")
|
|
12830
|
+
});
|
|
12831
|
+
const {
|
|
12832
|
+
factorId,
|
|
12833
|
+
code: code2
|
|
12834
|
+
} = parseBody2(verifySchema, await c.req.json());
|
|
12835
|
+
const factor = await authRepo.getMfaFactorById(factorId);
|
|
12836
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
12837
|
+
throw ApiError.notFound("MFA factor not found");
|
|
12838
|
+
}
|
|
12839
|
+
if (factor.verified) {
|
|
12840
|
+
throw ApiError.badRequest("Factor is already verified", "ALREADY_VERIFIED");
|
|
12841
|
+
}
|
|
12842
|
+
const secretBuffer = base32Decode(factor.secretEncrypted);
|
|
12843
|
+
const isValid2 = verifyTotp(secretBuffer, code2);
|
|
12844
|
+
if (!isValid2) {
|
|
12845
|
+
throw ApiError.unauthorized("Invalid TOTP code", "INVALID_CODE");
|
|
12846
|
+
}
|
|
12847
|
+
await authRepo.verifyMfaFactor(factorId);
|
|
12848
|
+
return c.json({
|
|
12849
|
+
success: true,
|
|
12850
|
+
message: "MFA factor verified and enrolled"
|
|
12851
|
+
});
|
|
12852
|
+
});
|
|
12853
|
+
router.post("/mfa/challenge", requireAuth, async (c) => {
|
|
12854
|
+
const userCtx = c.get("user");
|
|
12855
|
+
if (!userCtx) {
|
|
12856
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12857
|
+
}
|
|
12858
|
+
const challengeSchema = objectType({
|
|
12859
|
+
factorId: stringType().min(1, "Factor ID is required")
|
|
12860
|
+
});
|
|
12861
|
+
const {
|
|
12862
|
+
factorId
|
|
12863
|
+
} = parseBody2(challengeSchema, await c.req.json());
|
|
12864
|
+
const factor = await authRepo.getMfaFactorById(factorId);
|
|
12865
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
12866
|
+
throw ApiError.notFound("MFA factor not found");
|
|
12867
|
+
}
|
|
12868
|
+
if (!factor.verified) {
|
|
12869
|
+
throw ApiError.badRequest("MFA factor is not yet verified", "FACTOR_NOT_VERIFIED");
|
|
12870
|
+
}
|
|
12871
|
+
const ipAddress = c.req.header("x-forwarded-for") || "unknown";
|
|
12872
|
+
const challenge = await authRepo.createMfaChallenge(factorId, ipAddress);
|
|
12873
|
+
return c.json({
|
|
12874
|
+
challengeId: challenge.id,
|
|
12875
|
+
factorId: challenge.factorId,
|
|
12876
|
+
expiresAt: new Date(Date.now() + 5 * 60 * 1e3).toISOString()
|
|
12877
|
+
});
|
|
12878
|
+
});
|
|
12879
|
+
router.post("/mfa/challenge/verify", requireAuth, async (c) => {
|
|
12880
|
+
const userCtx = c.get("user");
|
|
12881
|
+
if (!userCtx) {
|
|
12882
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12883
|
+
}
|
|
12884
|
+
const challengeVerifySchema = objectType({
|
|
12885
|
+
challengeId: stringType().min(1, "Challenge ID is required"),
|
|
12886
|
+
code: stringType().min(1, "Code is required")
|
|
12887
|
+
});
|
|
12888
|
+
const {
|
|
12889
|
+
challengeId,
|
|
12890
|
+
code: code2
|
|
12891
|
+
} = parseBody2(challengeVerifySchema, await c.req.json());
|
|
12892
|
+
const challenge = await authRepo.getMfaChallengeById(challengeId);
|
|
12893
|
+
if (!challenge) {
|
|
12894
|
+
throw ApiError.badRequest("Invalid or expired challenge", "INVALID_CHALLENGE");
|
|
12895
|
+
}
|
|
12896
|
+
const factor = await authRepo.getMfaFactorById(challenge.factorId);
|
|
12897
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
12898
|
+
throw ApiError.notFound("MFA factor not found");
|
|
12899
|
+
}
|
|
12900
|
+
const isRecoveryCode = code2.length > 6;
|
|
12901
|
+
let isValid2 = false;
|
|
12902
|
+
if (isRecoveryCode) {
|
|
12903
|
+
const codeHash = hashRecoveryCode(code2);
|
|
12904
|
+
isValid2 = await authRepo.useRecoveryCode(userCtx.userId, codeHash);
|
|
12905
|
+
} else {
|
|
12906
|
+
const secretBuffer = base32Decode(factor.secretEncrypted);
|
|
12907
|
+
isValid2 = verifyTotp(secretBuffer, code2);
|
|
12908
|
+
}
|
|
12909
|
+
if (!isValid2) {
|
|
12910
|
+
throw ApiError.unauthorized("Invalid verification code", "INVALID_CODE");
|
|
12911
|
+
}
|
|
12912
|
+
await authRepo.verifyMfaChallenge(challengeId);
|
|
12913
|
+
const roles = await authRepo.getUserRoles(userCtx.userId);
|
|
12914
|
+
const roleIds = roles.map((r) => r.id);
|
|
12915
|
+
const accessToken = generateAccessToken(userCtx.userId, roleIds, "aal2");
|
|
12916
|
+
const refreshToken = generateRefreshToken();
|
|
12917
|
+
await authRepo.createRefreshToken(userCtx.userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
12918
|
+
if (authHooks?.onMfaVerified) {
|
|
12919
|
+
authHooks.onMfaVerified(userCtx.userId, factor.id).catch((err) => {
|
|
12920
|
+
console.error("[AuthHooks] onMfaVerified error:", err instanceof Error ? err.message : err);
|
|
12921
|
+
});
|
|
12922
|
+
}
|
|
12923
|
+
return c.json({
|
|
12924
|
+
tokens: {
|
|
12925
|
+
accessToken,
|
|
12926
|
+
refreshToken,
|
|
12927
|
+
accessTokenExpiresAt: getAccessTokenExpiry()
|
|
12928
|
+
}
|
|
12929
|
+
});
|
|
12930
|
+
});
|
|
12931
|
+
router.get("/mfa/factors", requireAuth, async (c) => {
|
|
12932
|
+
const userCtx = c.get("user");
|
|
12933
|
+
if (!userCtx) {
|
|
12934
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12935
|
+
}
|
|
12936
|
+
const factors = await authRepo.getMfaFactors(userCtx.userId);
|
|
12937
|
+
return c.json({
|
|
12938
|
+
factors: factors.map((f2) => ({
|
|
12939
|
+
id: f2.id,
|
|
12940
|
+
factorType: f2.factorType,
|
|
12941
|
+
friendlyName: f2.friendlyName,
|
|
12942
|
+
verified: f2.verified,
|
|
12943
|
+
createdAt: f2.createdAt
|
|
12944
|
+
}))
|
|
12945
|
+
});
|
|
12946
|
+
});
|
|
12947
|
+
router.delete("/mfa/unenroll", requireAuth, async (c) => {
|
|
12948
|
+
const userCtx = c.get("user");
|
|
12949
|
+
if (!userCtx) {
|
|
12950
|
+
throw ApiError.unauthorized("Not authenticated");
|
|
12951
|
+
}
|
|
12952
|
+
const unenrollSchema = objectType({
|
|
12953
|
+
factorId: stringType().min(1, "Factor ID is required")
|
|
12954
|
+
});
|
|
12955
|
+
const {
|
|
12956
|
+
factorId
|
|
12957
|
+
} = parseBody2(unenrollSchema, await c.req.json());
|
|
12958
|
+
const factor = await authRepo.getMfaFactorById(factorId);
|
|
12959
|
+
if (!factor || factor.userId !== userCtx.userId) {
|
|
12960
|
+
throw ApiError.notFound("MFA factor not found");
|
|
12961
|
+
}
|
|
12962
|
+
await authRepo.deleteMfaFactor(factorId, userCtx.userId);
|
|
12963
|
+
const hasFactors = await authRepo.hasVerifiedMfaFactors(userCtx.userId);
|
|
12964
|
+
if (!hasFactors) {
|
|
12965
|
+
await authRepo.deleteAllRecoveryCodes(userCtx.userId);
|
|
12966
|
+
}
|
|
12967
|
+
return c.json({
|
|
12968
|
+
success: true,
|
|
12969
|
+
message: "MFA factor removed"
|
|
12970
|
+
});
|
|
12971
|
+
});
|
|
11884
12972
|
return router;
|
|
11885
12973
|
}
|
|
11886
12974
|
function generateSecurePassword() {
|
|
@@ -11912,9 +13000,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11912
13000
|
emailService,
|
|
11913
13001
|
emailConfig,
|
|
11914
13002
|
hooks,
|
|
11915
|
-
|
|
13003
|
+
authHooks
|
|
11916
13004
|
} = config;
|
|
11917
|
-
const ops =
|
|
13005
|
+
const ops = resolveAuthHooks(authHooks);
|
|
11918
13006
|
function buildHookContext(c, method) {
|
|
11919
13007
|
const user = c.get("user");
|
|
11920
13008
|
return {
|
|
@@ -11982,6 +13070,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11982
13070
|
if (!userId) {
|
|
11983
13071
|
throw ApiError.unauthorized("User ID not found in auth context");
|
|
11984
13072
|
}
|
|
13073
|
+
const caller = await authRepo.getUserById(userId);
|
|
13074
|
+
if (!caller) {
|
|
13075
|
+
throw ApiError.notFound("Authenticated user does not exist in the database. Please sign out and sign in/register again.", "USER_NOT_FOUND");
|
|
13076
|
+
}
|
|
11985
13077
|
await authRepo.setUserRoles(userId, ["admin"]);
|
|
11986
13078
|
if (config.setBootstrapCompleted) {
|
|
11987
13079
|
await config.setBootstrapCompleted();
|
|
@@ -12233,6 +13325,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12233
13325
|
await authRepo.updateUser(userId, updates);
|
|
12234
13326
|
}
|
|
12235
13327
|
if (roles !== void 0 && Array.isArray(roles)) {
|
|
13328
|
+
const currentRoles = await authRepo.getUserRoleIds(userId);
|
|
13329
|
+
const wasAdmin = currentRoles.includes("admin");
|
|
13330
|
+
const willBeAdmin = roles.includes("admin");
|
|
13331
|
+
if (wasAdmin && !willBeAdmin) {
|
|
13332
|
+
const adminUsers = await authRepo.listUsersPaginated({
|
|
13333
|
+
roleId: "admin",
|
|
13334
|
+
limit: 1
|
|
13335
|
+
});
|
|
13336
|
+
if (adminUsers.total <= 1) {
|
|
13337
|
+
throw ApiError.forbidden("Cannot demote the last administrator", "LAST_ADMIN");
|
|
13338
|
+
}
|
|
13339
|
+
}
|
|
12236
13340
|
await authRepo.setUserRoles(userId, roles);
|
|
12237
13341
|
}
|
|
12238
13342
|
const result = await authRepo.getUserWithRoles(userId);
|
|
@@ -12257,6 +13361,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12257
13361
|
if (!existing) {
|
|
12258
13362
|
throw ApiError.notFound("User not found");
|
|
12259
13363
|
}
|
|
13364
|
+
const roles = await authRepo.getUserRoleIds(userId);
|
|
13365
|
+
if (roles.includes("admin")) {
|
|
13366
|
+
const adminUsers = await authRepo.listUsersPaginated({
|
|
13367
|
+
roleId: "admin",
|
|
13368
|
+
limit: 1
|
|
13369
|
+
});
|
|
13370
|
+
if (adminUsers.total <= 1) {
|
|
13371
|
+
throw ApiError.forbidden("Cannot delete the last administrator", "LAST_ADMIN");
|
|
13372
|
+
}
|
|
13373
|
+
}
|
|
12260
13374
|
const hookCtx = buildHookContext(c, "DELETE");
|
|
12261
13375
|
if (hooks?.users?.beforeDelete) {
|
|
12262
13376
|
await hooks.users.beforeDelete(userId, hookCtx);
|
|
@@ -12278,8 +13392,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12278
13392
|
id: r.id,
|
|
12279
13393
|
name: r.name,
|
|
12280
13394
|
isAdmin: r.isAdmin,
|
|
12281
|
-
defaultPermissions: r.defaultPermissions
|
|
12282
|
-
config: r.config
|
|
13395
|
+
defaultPermissions: r.defaultPermissions
|
|
12283
13396
|
}));
|
|
12284
13397
|
adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
|
|
12285
13398
|
return c.json({
|
|
@@ -12302,8 +13415,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12302
13415
|
id,
|
|
12303
13416
|
name: name2,
|
|
12304
13417
|
isAdmin,
|
|
12305
|
-
defaultPermissions
|
|
12306
|
-
config: config2
|
|
13418
|
+
defaultPermissions
|
|
12307
13419
|
} = body;
|
|
12308
13420
|
if (!id || !name2) {
|
|
12309
13421
|
throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
|
|
@@ -12316,8 +13428,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12316
13428
|
id,
|
|
12317
13429
|
name: name2,
|
|
12318
13430
|
isAdmin: isAdmin ?? false,
|
|
12319
|
-
defaultPermissions: defaultPermissions ?? null
|
|
12320
|
-
config: config2 ?? null
|
|
13431
|
+
defaultPermissions: defaultPermissions ?? null
|
|
12321
13432
|
});
|
|
12322
13433
|
return c.json({
|
|
12323
13434
|
role
|
|
@@ -12329,8 +13440,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12329
13440
|
const {
|
|
12330
13441
|
name: name2,
|
|
12331
13442
|
isAdmin,
|
|
12332
|
-
defaultPermissions
|
|
12333
|
-
config: config2
|
|
13443
|
+
defaultPermissions
|
|
12334
13444
|
} = body;
|
|
12335
13445
|
const existing = await authRepo.getRoleById(roleId);
|
|
12336
13446
|
if (!existing) {
|
|
@@ -12339,8 +13449,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12339
13449
|
const role = await authRepo.updateRole(roleId, {
|
|
12340
13450
|
name: name2,
|
|
12341
13451
|
isAdmin,
|
|
12342
|
-
defaultPermissions
|
|
12343
|
-
config: config2
|
|
13452
|
+
defaultPermissions
|
|
12344
13453
|
});
|
|
12345
13454
|
return c.json({
|
|
12346
13455
|
role
|
|
@@ -12372,9 +13481,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12372
13481
|
oauthProviders = [],
|
|
12373
13482
|
serviceKey,
|
|
12374
13483
|
hooks,
|
|
12375
|
-
|
|
13484
|
+
authHooks
|
|
12376
13485
|
} = config;
|
|
12377
|
-
const resolvedOps =
|
|
13486
|
+
const resolvedOps = resolveAuthHooks(authHooks);
|
|
12378
13487
|
const adapter = {
|
|
12379
13488
|
id: "rebase-builtin",
|
|
12380
13489
|
serviceKey,
|
|
@@ -12448,7 +13557,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12448
13557
|
rawToken: token
|
|
12449
13558
|
};
|
|
12450
13559
|
},
|
|
12451
|
-
userManagement: createUserManagementFromRepo(authRepository, resolvedOps,
|
|
13560
|
+
userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
|
|
12452
13561
|
roleManagement: createRoleManagementFromRepo(authRepository),
|
|
12453
13562
|
createAuthRoutes() {
|
|
12454
13563
|
return createAuthRoutes({
|
|
@@ -12458,7 +13567,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12458
13567
|
allowRegistration,
|
|
12459
13568
|
defaultRole,
|
|
12460
13569
|
oauthProviders,
|
|
12461
|
-
|
|
13570
|
+
authHooks
|
|
12462
13571
|
});
|
|
12463
13572
|
},
|
|
12464
13573
|
createAdminRoutes() {
|
|
@@ -12468,7 +13577,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12468
13577
|
emailConfig,
|
|
12469
13578
|
serviceKey,
|
|
12470
13579
|
hooks,
|
|
12471
|
-
|
|
13580
|
+
authHooks
|
|
12472
13581
|
});
|
|
12473
13582
|
},
|
|
12474
13583
|
async getCapabilities() {
|
|
@@ -12497,7 +13606,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12497
13606
|
};
|
|
12498
13607
|
return adapter;
|
|
12499
13608
|
}
|
|
12500
|
-
function createUserManagementFromRepo(repo, resolvedOps,
|
|
13609
|
+
function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
|
|
12501
13610
|
return {
|
|
12502
13611
|
async listUsers(options2) {
|
|
12503
13612
|
const result = await repo.listUsersPaginated({
|
|
@@ -12528,14 +13637,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12528
13637
|
photoUrl: data.photoUrl,
|
|
12529
13638
|
metadata: data.metadata
|
|
12530
13639
|
};
|
|
12531
|
-
if (
|
|
12532
|
-
createData = await
|
|
13640
|
+
if (authHooks?.beforeUserCreate) {
|
|
13641
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
12533
13642
|
}
|
|
12534
13643
|
const user = await repo.createUser(createData);
|
|
12535
|
-
if (
|
|
12536
|
-
|
|
12537
|
-
|
|
12538
|
-
})
|
|
13644
|
+
if (authHooks?.afterUserCreate) {
|
|
13645
|
+
try {
|
|
13646
|
+
await authHooks.afterUserCreate(user);
|
|
13647
|
+
} catch (err) {
|
|
13648
|
+
console.error("[AuthHooks] afterUserCreate error:", err instanceof Error ? err.message : err);
|
|
13649
|
+
}
|
|
12539
13650
|
}
|
|
12540
13651
|
return toAuthUserData(user);
|
|
12541
13652
|
},
|
|
@@ -12552,7 +13663,15 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12552
13663
|
return user ? toAuthUserData(user) : null;
|
|
12553
13664
|
},
|
|
12554
13665
|
async deleteUser(id) {
|
|
13666
|
+
if (authHooks?.beforeUserDelete) {
|
|
13667
|
+
await authHooks.beforeUserDelete(id);
|
|
13668
|
+
}
|
|
12555
13669
|
await repo.deleteUser(id);
|
|
13670
|
+
if (authHooks?.afterUserDelete) {
|
|
13671
|
+
authHooks.afterUserDelete(id).catch((err) => {
|
|
13672
|
+
console.error("[AuthHooks] afterUserDelete error:", err instanceof Error ? err.message : err);
|
|
13673
|
+
});
|
|
13674
|
+
}
|
|
12556
13675
|
},
|
|
12557
13676
|
async getUserRoles(userId) {
|
|
12558
13677
|
const roles = await repo.getUserRoles(userId);
|
|
@@ -12579,8 +13698,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12579
13698
|
name: data.name,
|
|
12580
13699
|
isAdmin: data.isAdmin,
|
|
12581
13700
|
defaultPermissions: data.defaultPermissions,
|
|
12582
|
-
collectionPermissions: data.collectionPermissions
|
|
12583
|
-
config: data.config
|
|
13701
|
+
collectionPermissions: data.collectionPermissions
|
|
12584
13702
|
});
|
|
12585
13703
|
return toAuthRoleData(role);
|
|
12586
13704
|
},
|
|
@@ -12611,8 +13729,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12611
13729
|
name: role.name,
|
|
12612
13730
|
isAdmin: role.isAdmin,
|
|
12613
13731
|
defaultPermissions: role.defaultPermissions,
|
|
12614
|
-
collectionPermissions: role.collectionPermissions
|
|
12615
|
-
config: role.config
|
|
13732
|
+
collectionPermissions: role.collectionPermissions
|
|
12616
13733
|
};
|
|
12617
13734
|
}
|
|
12618
13735
|
function configureLogLevel(logLevel) {
|
|
@@ -12633,20 +13750,20 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12633
13750
|
if (currentLevel < 0) console.error = () => {
|
|
12634
13751
|
};
|
|
12635
13752
|
}
|
|
13753
|
+
let originalConsole;
|
|
12636
13754
|
function resetConsole() {
|
|
12637
|
-
if (!
|
|
12638
|
-
|
|
13755
|
+
if (!originalConsole) {
|
|
13756
|
+
originalConsole = {
|
|
12639
13757
|
log: console.log,
|
|
12640
13758
|
warn: console.warn,
|
|
12641
13759
|
error: console.error,
|
|
12642
13760
|
debug: console.debug
|
|
12643
13761
|
};
|
|
12644
13762
|
}
|
|
12645
|
-
|
|
12646
|
-
console.
|
|
12647
|
-
console.
|
|
12648
|
-
console.
|
|
12649
|
-
console.debug = original.debug;
|
|
13763
|
+
console.log = originalConsole.log;
|
|
13764
|
+
console.warn = originalConsole.warn;
|
|
13765
|
+
console.error = originalConsole.error;
|
|
13766
|
+
console.debug = originalConsole.debug;
|
|
12650
13767
|
}
|
|
12651
13768
|
const GCP_SEVERITY = {
|
|
12652
13769
|
debug: "DEBUG",
|
|
@@ -12888,12 +14005,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12888
14005
|
exports3.Response = globalObject.Response;
|
|
12889
14006
|
})(browser$1, browser$1.exports);
|
|
12890
14007
|
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
14008
|
const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
|
|
12898
14009
|
isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
|
|
12899
14010
|
isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
|
|
@@ -13678,16 +14789,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13678
14789
|
value: true
|
|
13679
14790
|
});
|
|
13680
14791
|
sha1$1.default = void 0;
|
|
13681
|
-
function f(s2, x, y2,
|
|
14792
|
+
function f(s2, x, y2, z2) {
|
|
13682
14793
|
switch (s2) {
|
|
13683
14794
|
case 0:
|
|
13684
|
-
return x & y2 ^ ~x &
|
|
14795
|
+
return x & y2 ^ ~x & z2;
|
|
13685
14796
|
case 1:
|
|
13686
|
-
return x ^ y2 ^
|
|
14797
|
+
return x ^ y2 ^ z2;
|
|
13687
14798
|
case 2:
|
|
13688
|
-
return x & y2 ^ x &
|
|
14799
|
+
return x & y2 ^ x & z2 ^ y2 & z2;
|
|
13689
14800
|
case 3:
|
|
13690
|
-
return x ^ y2 ^
|
|
14801
|
+
return x ^ y2 ^ z2;
|
|
13691
14802
|
}
|
|
13692
14803
|
}
|
|
13693
14804
|
function ROTL(x, n) {
|
|
@@ -14736,7 +15847,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
14736
15847
|
const extend_1 = __importDefault(extend);
|
|
14737
15848
|
const https_1 = require$$1;
|
|
14738
15849
|
const node_fetch_1 = __importDefault(browserExports);
|
|
14739
|
-
const querystring_1$1 = __importDefault(require$$
|
|
15850
|
+
const querystring_1$1 = __importDefault(require$$1$2);
|
|
14740
15851
|
const is_stream_1 = __importDefault(isStream_1);
|
|
14741
15852
|
const url_1 = require$$0$2;
|
|
14742
15853
|
const common_1 = common$1;
|
|
@@ -16414,11 +17525,11 @@ Content-Type: ${partContentType}\r
|
|
|
16414
17525
|
return n > 0 || n === i2 ? i2 : i2 - 1;
|
|
16415
17526
|
}
|
|
16416
17527
|
function coeffToString(a2) {
|
|
16417
|
-
var s2,
|
|
17528
|
+
var s2, z2, i2 = 1, j = a2.length, r = a2[0] + "";
|
|
16418
17529
|
for (; i2 < j; ) {
|
|
16419
17530
|
s2 = a2[i2++] + "";
|
|
16420
|
-
|
|
16421
|
-
for (;
|
|
17531
|
+
z2 = LOG_BASE - s2.length;
|
|
17532
|
+
for (; z2--; s2 = "0" + s2) ;
|
|
16422
17533
|
r += s2;
|
|
16423
17534
|
}
|
|
16424
17535
|
for (j = r.length; r.charCodeAt(--j) === 48; ) ;
|
|
@@ -16451,15 +17562,15 @@ Content-Type: ${partContentType}\r
|
|
|
16451
17562
|
function toExponential(str, e) {
|
|
16452
17563
|
return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
|
|
16453
17564
|
}
|
|
16454
|
-
function toFixedPoint(str, e,
|
|
17565
|
+
function toFixedPoint(str, e, z2) {
|
|
16455
17566
|
var len2, zs;
|
|
16456
17567
|
if (e < 0) {
|
|
16457
|
-
for (zs =
|
|
17568
|
+
for (zs = z2 + "."; ++e; zs += z2) ;
|
|
16458
17569
|
str = zs + str;
|
|
16459
17570
|
} else {
|
|
16460
17571
|
len2 = str.length;
|
|
16461
17572
|
if (++e > len2) {
|
|
16462
|
-
for (zs =
|
|
17573
|
+
for (zs = z2, e -= len2; --e; zs += z2) ;
|
|
16463
17574
|
str += zs;
|
|
16464
17575
|
} else if (e < len2) {
|
|
16465
17576
|
str = str.slice(0, e) + "." + str.slice(e);
|
|
@@ -16878,7 +17989,7 @@ Content-Type: ${partContentType}\r
|
|
|
16878
17989
|
exports3.isGoogleComputeEngine = isGoogleComputeEngine;
|
|
16879
17990
|
exports3.detectGCPResidency = detectGCPResidency;
|
|
16880
17991
|
const fs_1 = fs$4;
|
|
16881
|
-
const os_1 = require$$1$
|
|
17992
|
+
const os_1 = require$$1$3;
|
|
16882
17993
|
exports3.GCE_LINUX_BIOS_PATHS = {
|
|
16883
17994
|
BIOS_DATE: "/sys/class/dmi/id/bios_date",
|
|
16884
17995
|
BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
|
|
@@ -17011,7 +18122,7 @@ Content-Type: ${partContentType}\r
|
|
|
17011
18122
|
exports3.setBackend = setBackend;
|
|
17012
18123
|
exports3.log = log;
|
|
17013
18124
|
const node_events_1 = require$$0$8;
|
|
17014
|
-
const process2 = __importStar2(require$$1$
|
|
18125
|
+
const process2 = __importStar2(require$$1$4);
|
|
17015
18126
|
const util2 = __importStar2(require$$2$1);
|
|
17016
18127
|
const colours_1 = colours;
|
|
17017
18128
|
var LogSeverity;
|
|
@@ -18083,7 +19194,7 @@ Content-Type: ${partContentType}\r
|
|
|
18083
19194
|
Object.defineProperty(oauth2client, "__esModule", { value: true });
|
|
18084
19195
|
oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
|
|
18085
19196
|
const gaxios_1$5 = src$2;
|
|
18086
|
-
const querystring$2 = require$$
|
|
19197
|
+
const querystring$2 = require$$1$2;
|
|
18087
19198
|
const stream$4 = require$$0$4;
|
|
18088
19199
|
const formatEcdsa = ecdsaSigFormatter;
|
|
18089
19200
|
const crypto_1$2 = requireCrypto();
|
|
@@ -19571,7 +20682,7 @@ Content-Type: ${partContentType}\r
|
|
|
19571
20682
|
Object.defineProperty(refreshclient, "__esModule", { value: true });
|
|
19572
20683
|
refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
|
|
19573
20684
|
const oauth2client_1$1 = oauth2client;
|
|
19574
|
-
const querystring_1 = require$$
|
|
20685
|
+
const querystring_1 = require$$1$2;
|
|
19575
20686
|
refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
|
|
19576
20687
|
class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
|
|
19577
20688
|
constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
|
|
@@ -19846,7 +20957,7 @@ Content-Type: ${partContentType}\r
|
|
|
19846
20957
|
Object.defineProperty(oauth2common, "__esModule", { value: true });
|
|
19847
20958
|
oauth2common.OAuthClientAuthHandler = void 0;
|
|
19848
20959
|
oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
|
|
19849
|
-
const querystring$1 = require$$
|
|
20960
|
+
const querystring$1 = require$$1$2;
|
|
19850
20961
|
const crypto_1$1 = requireCrypto();
|
|
19851
20962
|
const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
|
|
19852
20963
|
class OAuthClientAuthHandler {
|
|
@@ -19992,7 +21103,7 @@ Content-Type: ${partContentType}\r
|
|
|
19992
21103
|
Object.defineProperty(stscredentials, "__esModule", { value: true });
|
|
19993
21104
|
stscredentials.StsCredentials = void 0;
|
|
19994
21105
|
const gaxios_1$1 = src$2;
|
|
19995
|
-
const querystring = require$$
|
|
21106
|
+
const querystring = require$$1$2;
|
|
19996
21107
|
const transporters_1 = transporters;
|
|
19997
21108
|
const oauth2common_1$1 = oauth2common;
|
|
19998
21109
|
class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
|
|
@@ -21598,7 +22709,7 @@ ${credentialScope}
|
|
|
21598
22709
|
const child_process_1 = require$$0$a;
|
|
21599
22710
|
const fs2 = fs$4;
|
|
21600
22711
|
const gcpMetadata2 = src$3;
|
|
21601
|
-
const os2 = require$$1$
|
|
22712
|
+
const os2 = require$$1$3;
|
|
21602
22713
|
const path2 = path$3;
|
|
21603
22714
|
const crypto_12 = requireCrypto();
|
|
21604
22715
|
const transporters_12 = transporters;
|
|
@@ -22962,7 +24073,7 @@ ${credentialScope}
|
|
|
22962
24073
|
}
|
|
22963
24074
|
function createAppleProvider(config) {
|
|
22964
24075
|
async function generateClientSecret() {
|
|
22965
|
-
return jwt.sign({}, config.privateKey, {
|
|
24076
|
+
return jwt$1.sign({}, config.privateKey, {
|
|
22966
24077
|
algorithm: "ES256",
|
|
22967
24078
|
keyid: config.keyId,
|
|
22968
24079
|
issuer: config.teamId,
|
|
@@ -23442,6 +24553,341 @@ ${credentialScope}
|
|
|
23442
24553
|
}
|
|
23443
24554
|
};
|
|
23444
24555
|
}
|
|
24556
|
+
const TABLE$1 = "rebase.api_keys";
|
|
24557
|
+
function generateApiKey() {
|
|
24558
|
+
const random = require$$0$5.randomBytes(16).toString("hex");
|
|
24559
|
+
return `rk_live_${random}`;
|
|
24560
|
+
}
|
|
24561
|
+
function hashKey(plaintext) {
|
|
24562
|
+
return require$$0$5.createHash("sha256").update(plaintext).digest("hex");
|
|
24563
|
+
}
|
|
24564
|
+
function keyPrefix(plaintext) {
|
|
24565
|
+
return plaintext.substring(0, 12);
|
|
24566
|
+
}
|
|
24567
|
+
function toMasked(row) {
|
|
24568
|
+
return {
|
|
24569
|
+
id: row.id,
|
|
24570
|
+
name: row.name,
|
|
24571
|
+
key_prefix: row.key_prefix,
|
|
24572
|
+
permissions: row.permissions,
|
|
24573
|
+
rate_limit: row.rate_limit,
|
|
24574
|
+
created_by: row.created_by,
|
|
24575
|
+
created_at: row.created_at,
|
|
24576
|
+
updated_at: row.updated_at,
|
|
24577
|
+
last_used_at: row.last_used_at,
|
|
24578
|
+
expires_at: row.expires_at,
|
|
24579
|
+
revoked_at: row.revoked_at
|
|
24580
|
+
};
|
|
24581
|
+
}
|
|
24582
|
+
function rowToApiKey(row) {
|
|
24583
|
+
const permissions = typeof row.permissions === "string" ? JSON.parse(row.permissions) : row.permissions ?? [];
|
|
24584
|
+
return {
|
|
24585
|
+
id: row.id,
|
|
24586
|
+
name: row.name,
|
|
24587
|
+
key_prefix: row.key_prefix,
|
|
24588
|
+
key_hash: row.key_hash,
|
|
24589
|
+
permissions,
|
|
24590
|
+
rate_limit: row.rate_limit !== null && row.rate_limit !== void 0 ? Number(row.rate_limit) : null,
|
|
24591
|
+
created_by: row.created_by,
|
|
24592
|
+
created_at: new Date(row.created_at).toISOString(),
|
|
24593
|
+
updated_at: new Date(row.updated_at).toISOString(),
|
|
24594
|
+
last_used_at: row.last_used_at ? new Date(row.last_used_at).toISOString() : null,
|
|
24595
|
+
expires_at: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
24596
|
+
revoked_at: row.revoked_at ? new Date(row.revoked_at).toISOString() : null
|
|
24597
|
+
};
|
|
24598
|
+
}
|
|
24599
|
+
function esc(value) {
|
|
24600
|
+
return value.replace(/'/g, "''");
|
|
24601
|
+
}
|
|
24602
|
+
function createApiKeyStore(driver) {
|
|
24603
|
+
const admin = driver.admin;
|
|
24604
|
+
if (!isSQLAdmin(admin)) {
|
|
24605
|
+
logger.warn("⚠️ [api-key-store] DataDriver does not support SQL admin — API keys will not be available.");
|
|
24606
|
+
return void 0;
|
|
24607
|
+
}
|
|
24608
|
+
const exec = admin.executeSql.bind(admin);
|
|
24609
|
+
return {
|
|
24610
|
+
// ── Schema bootstrap ────────────────────────────────────────
|
|
24611
|
+
async ensureTable() {
|
|
24612
|
+
try {
|
|
24613
|
+
await exec("CREATE SCHEMA IF NOT EXISTS rebase");
|
|
24614
|
+
await exec(`
|
|
24615
|
+
CREATE TABLE IF NOT EXISTS ${TABLE$1} (
|
|
24616
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
24617
|
+
name TEXT NOT NULL,
|
|
24618
|
+
key_prefix TEXT NOT NULL,
|
|
24619
|
+
key_hash TEXT NOT NULL UNIQUE,
|
|
24620
|
+
permissions JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
24621
|
+
rate_limit INTEGER,
|
|
24622
|
+
created_by TEXT NOT NULL,
|
|
24623
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
24624
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
24625
|
+
last_used_at TIMESTAMPTZ,
|
|
24626
|
+
expires_at TIMESTAMPTZ,
|
|
24627
|
+
revoked_at TIMESTAMPTZ
|
|
24628
|
+
)
|
|
24629
|
+
`);
|
|
24630
|
+
await exec(`
|
|
24631
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_hash
|
|
24632
|
+
ON ${TABLE$1}(key_hash)
|
|
24633
|
+
`);
|
|
24634
|
+
await exec(`
|
|
24635
|
+
CREATE INDEX IF NOT EXISTS idx_api_keys_prefix
|
|
24636
|
+
ON ${TABLE$1}(key_prefix)
|
|
24637
|
+
`);
|
|
24638
|
+
logger.info("✅ API keys table ready");
|
|
24639
|
+
} catch (err) {
|
|
24640
|
+
logger.error("❌ Failed to create API keys table", {
|
|
24641
|
+
error: err
|
|
24642
|
+
});
|
|
24643
|
+
logger.warn("⚠️ Continuing without API keys support.");
|
|
24644
|
+
}
|
|
24645
|
+
},
|
|
24646
|
+
// ── Create ──────────────────────────────────────────────────
|
|
24647
|
+
async createApiKey(request, createdBy) {
|
|
24648
|
+
const plaintext = generateApiKey();
|
|
24649
|
+
const hash = hashKey(plaintext);
|
|
24650
|
+
const prefix = keyPrefix(plaintext);
|
|
24651
|
+
const permissionsJson = JSON.stringify(request.permissions);
|
|
24652
|
+
const rateLimitSql = request.rate_limit !== null && request.rate_limit !== void 0 ? String(request.rate_limit) : "NULL";
|
|
24653
|
+
const expiresAtSql = request.expires_at ? `'${esc(request.expires_at)}'` : "NULL";
|
|
24654
|
+
const rows = await exec(`
|
|
24655
|
+
INSERT INTO ${TABLE$1} (name, key_prefix, key_hash, permissions, rate_limit, created_by, expires_at)
|
|
24656
|
+
VALUES (
|
|
24657
|
+
'${esc(request.name)}',
|
|
24658
|
+
'${esc(prefix)}',
|
|
24659
|
+
'${esc(hash)}',
|
|
24660
|
+
'${esc(permissionsJson)}'::jsonb,
|
|
24661
|
+
${rateLimitSql},
|
|
24662
|
+
'${esc(createdBy)}',
|
|
24663
|
+
${expiresAtSql}
|
|
24664
|
+
)
|
|
24665
|
+
RETURNING *
|
|
24666
|
+
`);
|
|
24667
|
+
const apiKey = rowToApiKey(rows[0]);
|
|
24668
|
+
return {
|
|
24669
|
+
...toMasked(apiKey),
|
|
24670
|
+
key: plaintext
|
|
24671
|
+
};
|
|
24672
|
+
},
|
|
24673
|
+
// ── Lookup by hash ──────────────────────────────────────────
|
|
24674
|
+
async findByKeyHash(hash) {
|
|
24675
|
+
const rows = await exec(`
|
|
24676
|
+
SELECT * FROM ${TABLE$1}
|
|
24677
|
+
WHERE key_hash = '${esc(hash)}'
|
|
24678
|
+
LIMIT 1
|
|
24679
|
+
`);
|
|
24680
|
+
if (rows.length === 0) return null;
|
|
24681
|
+
return rowToApiKey(rows[0]);
|
|
24682
|
+
},
|
|
24683
|
+
// ── List all (masked) ───────────────────────────────────────
|
|
24684
|
+
async listApiKeys() {
|
|
24685
|
+
const rows = await exec(`
|
|
24686
|
+
SELECT * FROM ${TABLE$1}
|
|
24687
|
+
ORDER BY created_at DESC
|
|
24688
|
+
`);
|
|
24689
|
+
return rows.map((r) => toMasked(rowToApiKey(r)));
|
|
24690
|
+
},
|
|
24691
|
+
// ── Get by ID (masked) ──────────────────────────────────────
|
|
24692
|
+
async getApiKeyById(id) {
|
|
24693
|
+
const rows = await exec(`
|
|
24694
|
+
SELECT * FROM ${TABLE$1}
|
|
24695
|
+
WHERE id = '${esc(id)}'
|
|
24696
|
+
LIMIT 1
|
|
24697
|
+
`);
|
|
24698
|
+
if (rows.length === 0) return null;
|
|
24699
|
+
return toMasked(rowToApiKey(rows[0]));
|
|
24700
|
+
},
|
|
24701
|
+
// ── Update ──────────────────────────────────────────────────
|
|
24702
|
+
async updateApiKey(id, updates) {
|
|
24703
|
+
const setClauses = [];
|
|
24704
|
+
if (updates.name !== void 0) {
|
|
24705
|
+
setClauses.push(`name = '${esc(updates.name)}'`);
|
|
24706
|
+
}
|
|
24707
|
+
if (updates.permissions !== void 0) {
|
|
24708
|
+
setClauses.push(`permissions = '${esc(JSON.stringify(updates.permissions))}'::jsonb`);
|
|
24709
|
+
}
|
|
24710
|
+
if (updates.rate_limit !== void 0) {
|
|
24711
|
+
setClauses.push(updates.rate_limit !== null ? `rate_limit = ${updates.rate_limit}` : "rate_limit = NULL");
|
|
24712
|
+
}
|
|
24713
|
+
if (updates.expires_at !== void 0) {
|
|
24714
|
+
setClauses.push(updates.expires_at !== null ? `expires_at = '${esc(updates.expires_at)}'` : "expires_at = NULL");
|
|
24715
|
+
}
|
|
24716
|
+
if (setClauses.length === 0) {
|
|
24717
|
+
return this.getApiKeyById(id);
|
|
24718
|
+
}
|
|
24719
|
+
setClauses.push("updated_at = NOW()");
|
|
24720
|
+
const rows = await exec(`
|
|
24721
|
+
UPDATE ${TABLE$1}
|
|
24722
|
+
SET ${setClauses.join(", ")}
|
|
24723
|
+
WHERE id = '${esc(id)}'
|
|
24724
|
+
RETURNING *
|
|
24725
|
+
`);
|
|
24726
|
+
if (rows.length === 0) return null;
|
|
24727
|
+
return toMasked(rowToApiKey(rows[0]));
|
|
24728
|
+
},
|
|
24729
|
+
// ── Revoke (soft-delete) ────────────────────────────────────
|
|
24730
|
+
async revokeApiKey(id) {
|
|
24731
|
+
const rows = await exec(`
|
|
24732
|
+
UPDATE ${TABLE$1}
|
|
24733
|
+
SET revoked_at = NOW(), updated_at = NOW()
|
|
24734
|
+
WHERE id = '${esc(id)}' AND revoked_at IS NULL
|
|
24735
|
+
RETURNING id
|
|
24736
|
+
`);
|
|
24737
|
+
return rows.length > 0;
|
|
24738
|
+
},
|
|
24739
|
+
// ── Touch last_used_at ──────────────────────────────────────
|
|
24740
|
+
async updateLastUsed(id) {
|
|
24741
|
+
try {
|
|
24742
|
+
await exec(`
|
|
24743
|
+
UPDATE ${TABLE$1}
|
|
24744
|
+
SET last_used_at = NOW()
|
|
24745
|
+
WHERE id = '${esc(id)}'
|
|
24746
|
+
`);
|
|
24747
|
+
} catch (err) {
|
|
24748
|
+
logger.error("[api-key-store] Failed to update last_used_at", {
|
|
24749
|
+
error: err
|
|
24750
|
+
});
|
|
24751
|
+
}
|
|
24752
|
+
}
|
|
24753
|
+
};
|
|
24754
|
+
}
|
|
24755
|
+
function validatePermissions(permissions) {
|
|
24756
|
+
if (!Array.isArray(permissions)) return false;
|
|
24757
|
+
for (const perm of permissions) {
|
|
24758
|
+
if (typeof perm !== "object" || perm === null) return false;
|
|
24759
|
+
if (typeof perm.collection !== "string" || perm.collection.length === 0) return false;
|
|
24760
|
+
if (!Array.isArray(perm.operations) || perm.operations.length === 0) return false;
|
|
24761
|
+
const validOps = /* @__PURE__ */ new Set(["read", "write", "delete"]);
|
|
24762
|
+
for (const op of perm.operations) {
|
|
24763
|
+
if (!validOps.has(op)) return false;
|
|
24764
|
+
}
|
|
24765
|
+
}
|
|
24766
|
+
return true;
|
|
24767
|
+
}
|
|
24768
|
+
function createApiKeyRoutes(options2) {
|
|
24769
|
+
const {
|
|
24770
|
+
store,
|
|
24771
|
+
serviceKey
|
|
24772
|
+
} = options2;
|
|
24773
|
+
const router = new hono.Hono();
|
|
24774
|
+
router.onError(errorHandler);
|
|
24775
|
+
router.use("/*", createRequireAuth({
|
|
24776
|
+
serviceKey
|
|
24777
|
+
}));
|
|
24778
|
+
router.use("/*", requireAdmin);
|
|
24779
|
+
router.get("/", async (c) => {
|
|
24780
|
+
const keys2 = await store.listApiKeys();
|
|
24781
|
+
return c.json({
|
|
24782
|
+
keys: keys2
|
|
24783
|
+
});
|
|
24784
|
+
});
|
|
24785
|
+
router.post("/", async (c) => {
|
|
24786
|
+
const body = await c.req.json();
|
|
24787
|
+
const {
|
|
24788
|
+
name: name2,
|
|
24789
|
+
permissions,
|
|
24790
|
+
rate_limit,
|
|
24791
|
+
expires_at
|
|
24792
|
+
} = body;
|
|
24793
|
+
if (!name2 || typeof name2 !== "string" || name2.trim().length === 0) {
|
|
24794
|
+
throw ApiError.badRequest("Name is required", "INVALID_INPUT");
|
|
24795
|
+
}
|
|
24796
|
+
if (!validatePermissions(permissions)) {
|
|
24797
|
+
throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
|
|
24798
|
+
}
|
|
24799
|
+
if (rate_limit !== void 0 && rate_limit !== null) {
|
|
24800
|
+
if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
|
|
24801
|
+
throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
|
|
24802
|
+
}
|
|
24803
|
+
}
|
|
24804
|
+
if (expires_at !== void 0 && expires_at !== null) {
|
|
24805
|
+
const parsed = new Date(expires_at);
|
|
24806
|
+
if (isNaN(parsed.getTime())) {
|
|
24807
|
+
throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
|
|
24808
|
+
}
|
|
24809
|
+
if (parsed <= /* @__PURE__ */ new Date()) {
|
|
24810
|
+
throw ApiError.badRequest("expires_at must be in the future", "INVALID_INPUT");
|
|
24811
|
+
}
|
|
24812
|
+
}
|
|
24813
|
+
const user = c.get("user");
|
|
24814
|
+
const createdBy = user && typeof user === "object" && "userId" in user ? user.userId : "unknown";
|
|
24815
|
+
const request = {
|
|
24816
|
+
name: name2.trim(),
|
|
24817
|
+
permissions,
|
|
24818
|
+
rate_limit: rate_limit ?? null,
|
|
24819
|
+
expires_at: expires_at ?? null
|
|
24820
|
+
};
|
|
24821
|
+
const keyWithSecret = await store.createApiKey(request, createdBy);
|
|
24822
|
+
return c.json({
|
|
24823
|
+
key: keyWithSecret
|
|
24824
|
+
}, 201);
|
|
24825
|
+
});
|
|
24826
|
+
router.get("/:id", async (c) => {
|
|
24827
|
+
const id = c.req.param("id");
|
|
24828
|
+
const key = await store.getApiKeyById(id);
|
|
24829
|
+
if (!key) {
|
|
24830
|
+
throw ApiError.notFound("API key not found");
|
|
24831
|
+
}
|
|
24832
|
+
return c.json({
|
|
24833
|
+
key
|
|
24834
|
+
});
|
|
24835
|
+
});
|
|
24836
|
+
router.put("/:id", async (c) => {
|
|
24837
|
+
const id = c.req.param("id");
|
|
24838
|
+
const body = await c.req.json();
|
|
24839
|
+
const {
|
|
24840
|
+
name: name2,
|
|
24841
|
+
permissions,
|
|
24842
|
+
rate_limit,
|
|
24843
|
+
expires_at
|
|
24844
|
+
} = body;
|
|
24845
|
+
if (name2 !== void 0) {
|
|
24846
|
+
if (typeof name2 !== "string" || name2.trim().length === 0) {
|
|
24847
|
+
throw ApiError.badRequest("Name must be a non-empty string", "INVALID_INPUT");
|
|
24848
|
+
}
|
|
24849
|
+
}
|
|
24850
|
+
if (permissions !== void 0) {
|
|
24851
|
+
if (!validatePermissions(permissions)) {
|
|
24852
|
+
throw ApiError.badRequest("Permissions must be an array of { collection: string, operations: ('read' | 'write' | 'delete')[] }", "INVALID_INPUT");
|
|
24853
|
+
}
|
|
24854
|
+
}
|
|
24855
|
+
if (rate_limit !== void 0 && rate_limit !== null) {
|
|
24856
|
+
if (typeof rate_limit !== "number" || rate_limit < 1 || !Number.isInteger(rate_limit)) {
|
|
24857
|
+
throw ApiError.badRequest("rate_limit must be a positive integer or null", "INVALID_INPUT");
|
|
24858
|
+
}
|
|
24859
|
+
}
|
|
24860
|
+
if (expires_at !== void 0 && expires_at !== null) {
|
|
24861
|
+
const parsed = new Date(expires_at);
|
|
24862
|
+
if (isNaN(parsed.getTime())) {
|
|
24863
|
+
throw ApiError.badRequest("expires_at must be a valid ISO-8601 date", "INVALID_INPUT");
|
|
24864
|
+
}
|
|
24865
|
+
}
|
|
24866
|
+
const updates = {};
|
|
24867
|
+
if (name2 !== void 0) updates.name = name2.trim();
|
|
24868
|
+
if (permissions !== void 0) updates.permissions = permissions;
|
|
24869
|
+
if (rate_limit !== void 0) updates.rate_limit = rate_limit;
|
|
24870
|
+
if (expires_at !== void 0) updates.expires_at = expires_at;
|
|
24871
|
+
const key = await store.updateApiKey(id, updates);
|
|
24872
|
+
if (!key) {
|
|
24873
|
+
throw ApiError.notFound("API key not found");
|
|
24874
|
+
}
|
|
24875
|
+
return c.json({
|
|
24876
|
+
key
|
|
24877
|
+
});
|
|
24878
|
+
});
|
|
24879
|
+
router.delete("/:id", async (c) => {
|
|
24880
|
+
const id = c.req.param("id");
|
|
24881
|
+
const revoked = await store.revokeApiKey(id);
|
|
24882
|
+
if (!revoked) {
|
|
24883
|
+
throw ApiError.notFound("API key not found or already revoked");
|
|
24884
|
+
}
|
|
24885
|
+
return c.json({
|
|
24886
|
+
success: true
|
|
24887
|
+
});
|
|
24888
|
+
});
|
|
24889
|
+
return router;
|
|
24890
|
+
}
|
|
23445
24891
|
function createCustomAuthAdapter(options2) {
|
|
23446
24892
|
const defaultCapabilities = {
|
|
23447
24893
|
hasBuiltInAuthRoutes: false,
|
|
@@ -23476,9 +24922,13 @@ ${credentialScope}
|
|
|
23476
24922
|
}
|
|
23477
24923
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23478
24924
|
__proto__: null,
|
|
24925
|
+
apiKeyKeyGenerator,
|
|
23479
24926
|
configureJwt,
|
|
23480
24927
|
createAdapterAuthMiddleware,
|
|
23481
24928
|
createAdminRoutes,
|
|
24929
|
+
createApiKeyRateLimiter,
|
|
24930
|
+
createApiKeyRoutes,
|
|
24931
|
+
createApiKeyStore,
|
|
23482
24932
|
createAppleProvider,
|
|
23483
24933
|
createAuthMiddleware,
|
|
23484
24934
|
createAuthRoutes,
|
|
@@ -23504,11 +24954,15 @@ ${credentialScope}
|
|
|
23504
24954
|
getRefreshTokenExpiry,
|
|
23505
24955
|
hashPassword,
|
|
23506
24956
|
hashRefreshToken,
|
|
24957
|
+
httpMethodToOperation,
|
|
24958
|
+
isApiKeyToken,
|
|
24959
|
+
isOperationAllowed,
|
|
23507
24960
|
optionalAuth,
|
|
23508
24961
|
requireAdmin,
|
|
23509
24962
|
requireAuth,
|
|
23510
|
-
|
|
24963
|
+
resolveAuthHooks,
|
|
23511
24964
|
strictAuthLimiter,
|
|
24965
|
+
validateApiKey,
|
|
23512
24966
|
validatePasswordStrength,
|
|
23513
24967
|
verifyAccessToken,
|
|
23514
24968
|
verifyPassword
|
|
@@ -24033,6 +25487,409 @@ ${credentialScope}
|
|
|
24033
25487
|
};
|
|
24034
25488
|
}
|
|
24035
25489
|
}
|
|
25490
|
+
let sharpFactory;
|
|
25491
|
+
async function getSharp() {
|
|
25492
|
+
if (!sharpFactory) {
|
|
25493
|
+
try {
|
|
25494
|
+
const mod = await import("sharp");
|
|
25495
|
+
sharpFactory = mod.default;
|
|
25496
|
+
} catch (err) {
|
|
25497
|
+
throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
|
|
25498
|
+
}
|
|
25499
|
+
}
|
|
25500
|
+
if (!sharpFactory) {
|
|
25501
|
+
throw new Error("Failed to load optional 'sharp' dependency for image transformation.");
|
|
25502
|
+
}
|
|
25503
|
+
return sharpFactory;
|
|
25504
|
+
}
|
|
25505
|
+
const MAX_DIMENSION = 4096;
|
|
25506
|
+
const MAX_QUALITY = 100;
|
|
25507
|
+
const MIN_QUALITY = 1;
|
|
25508
|
+
const VALID_FORMATS = /* @__PURE__ */ new Set(["webp", "avif", "jpeg", "png"]);
|
|
25509
|
+
const VALID_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
|
|
25510
|
+
function parseTransformOptions(query) {
|
|
25511
|
+
const opts = {};
|
|
25512
|
+
let hasTransform = false;
|
|
25513
|
+
if (query.width) {
|
|
25514
|
+
const w2 = parseInt(query.width, 10);
|
|
25515
|
+
if (!Number.isNaN(w2) && w2 > 0) {
|
|
25516
|
+
opts.width = Math.min(w2, MAX_DIMENSION);
|
|
25517
|
+
hasTransform = true;
|
|
25518
|
+
}
|
|
25519
|
+
}
|
|
25520
|
+
if (query.height) {
|
|
25521
|
+
const h2 = parseInt(query.height, 10);
|
|
25522
|
+
if (!Number.isNaN(h2) && h2 > 0) {
|
|
25523
|
+
opts.height = Math.min(h2, MAX_DIMENSION);
|
|
25524
|
+
hasTransform = true;
|
|
25525
|
+
}
|
|
25526
|
+
}
|
|
25527
|
+
if (query.quality) {
|
|
25528
|
+
const q = parseInt(query.quality, 10);
|
|
25529
|
+
if (!Number.isNaN(q)) {
|
|
25530
|
+
opts.quality = Math.min(Math.max(q, MIN_QUALITY), MAX_QUALITY);
|
|
25531
|
+
hasTransform = true;
|
|
25532
|
+
}
|
|
25533
|
+
}
|
|
25534
|
+
if (query.format && VALID_FORMATS.has(query.format)) {
|
|
25535
|
+
opts.format = query.format;
|
|
25536
|
+
hasTransform = true;
|
|
25537
|
+
}
|
|
25538
|
+
if (query.fit && VALID_FITS.has(query.fit)) {
|
|
25539
|
+
opts.fit = query.fit;
|
|
25540
|
+
hasTransform = true;
|
|
25541
|
+
}
|
|
25542
|
+
return hasTransform ? opts : null;
|
|
25543
|
+
}
|
|
25544
|
+
const FORMAT_CONTENT_TYPES = {
|
|
25545
|
+
webp: "image/webp",
|
|
25546
|
+
avif: "image/avif",
|
|
25547
|
+
jpeg: "image/jpeg",
|
|
25548
|
+
png: "image/png"
|
|
25549
|
+
};
|
|
25550
|
+
function isTransformableImage(contentType) {
|
|
25551
|
+
return contentType.startsWith("image/") && !contentType.includes("svg") && !contentType.includes("gif");
|
|
25552
|
+
}
|
|
25553
|
+
async function transformImage(buffer, options2) {
|
|
25554
|
+
const sharp = await getSharp();
|
|
25555
|
+
let pipeline = sharp(buffer);
|
|
25556
|
+
if (options2.width || options2.height) {
|
|
25557
|
+
pipeline = pipeline.resize({
|
|
25558
|
+
width: options2.width,
|
|
25559
|
+
height: options2.height,
|
|
25560
|
+
fit: options2.fit || "cover",
|
|
25561
|
+
withoutEnlargement: true
|
|
25562
|
+
});
|
|
25563
|
+
}
|
|
25564
|
+
const format = options2.format || "webp";
|
|
25565
|
+
const quality = options2.quality || 80;
|
|
25566
|
+
switch (format) {
|
|
25567
|
+
case "webp":
|
|
25568
|
+
pipeline = pipeline.webp({
|
|
25569
|
+
quality
|
|
25570
|
+
});
|
|
25571
|
+
break;
|
|
25572
|
+
case "avif":
|
|
25573
|
+
pipeline = pipeline.avif({
|
|
25574
|
+
quality
|
|
25575
|
+
});
|
|
25576
|
+
break;
|
|
25577
|
+
case "jpeg":
|
|
25578
|
+
pipeline = pipeline.jpeg({
|
|
25579
|
+
quality
|
|
25580
|
+
});
|
|
25581
|
+
break;
|
|
25582
|
+
case "png":
|
|
25583
|
+
pipeline = pipeline.png({
|
|
25584
|
+
quality
|
|
25585
|
+
});
|
|
25586
|
+
break;
|
|
25587
|
+
}
|
|
25588
|
+
const data = await pipeline.toBuffer();
|
|
25589
|
+
return {
|
|
25590
|
+
data,
|
|
25591
|
+
contentType: FORMAT_CONTENT_TYPES[format]
|
|
25592
|
+
};
|
|
25593
|
+
}
|
|
25594
|
+
class TransformCache {
|
|
25595
|
+
cache = /* @__PURE__ */ new Map();
|
|
25596
|
+
maxEntries;
|
|
25597
|
+
maxAgeMs;
|
|
25598
|
+
constructor(maxEntries = 500, maxAgeMs = 36e5) {
|
|
25599
|
+
this.maxEntries = maxEntries;
|
|
25600
|
+
this.maxAgeMs = maxAgeMs;
|
|
25601
|
+
}
|
|
25602
|
+
/** Build a deterministic cache key from file key + transform options. */
|
|
25603
|
+
buildKey(fileKey, options2) {
|
|
25604
|
+
return `${fileKey}::${JSON.stringify(options2)}`;
|
|
25605
|
+
}
|
|
25606
|
+
get(cacheKey) {
|
|
25607
|
+
const entry = this.cache.get(cacheKey);
|
|
25608
|
+
if (!entry) return null;
|
|
25609
|
+
if (Date.now() - entry.timestamp > this.maxAgeMs) {
|
|
25610
|
+
this.cache.delete(cacheKey);
|
|
25611
|
+
return null;
|
|
25612
|
+
}
|
|
25613
|
+
this.cache.delete(cacheKey);
|
|
25614
|
+
this.cache.set(cacheKey, entry);
|
|
25615
|
+
return {
|
|
25616
|
+
data: entry.data,
|
|
25617
|
+
contentType: entry.contentType
|
|
25618
|
+
};
|
|
25619
|
+
}
|
|
25620
|
+
set(cacheKey, data, contentType) {
|
|
25621
|
+
if (this.cache.size >= this.maxEntries) {
|
|
25622
|
+
const oldest = this.cache.keys().next().value;
|
|
25623
|
+
if (oldest !== void 0) this.cache.delete(oldest);
|
|
25624
|
+
}
|
|
25625
|
+
this.cache.set(cacheKey, {
|
|
25626
|
+
data,
|
|
25627
|
+
contentType,
|
|
25628
|
+
timestamp: Date.now()
|
|
25629
|
+
});
|
|
25630
|
+
}
|
|
25631
|
+
}
|
|
25632
|
+
const MAX_UPLOAD_SIZE = 5 * 1024 * 1024 * 1024;
|
|
25633
|
+
const UPLOAD_EXPIRY_MS = 24 * 60 * 60 * 1e3;
|
|
25634
|
+
class TusHandler {
|
|
25635
|
+
constructor(storageBaseDir, storageController) {
|
|
25636
|
+
this.storageController = storageController;
|
|
25637
|
+
this.tusDir = path$3.join(storageBaseDir, ".tus-uploads");
|
|
25638
|
+
}
|
|
25639
|
+
uploads = /* @__PURE__ */ new Map();
|
|
25640
|
+
tusDir;
|
|
25641
|
+
cleanupTimer;
|
|
25642
|
+
/** Ensure the temp directory exists. */
|
|
25643
|
+
async ensureDir() {
|
|
25644
|
+
if (!fs$4.existsSync(this.tusDir)) {
|
|
25645
|
+
await promises.mkdir(this.tusDir, {
|
|
25646
|
+
recursive: true
|
|
25647
|
+
});
|
|
25648
|
+
}
|
|
25649
|
+
}
|
|
25650
|
+
/** Start periodic cleanup of stale uploads. */
|
|
25651
|
+
startCleanup() {
|
|
25652
|
+
if (this.cleanupTimer) return;
|
|
25653
|
+
this.cleanupTimer = setInterval(() => {
|
|
25654
|
+
void this.cleanupStale();
|
|
25655
|
+
}, 6e4);
|
|
25656
|
+
}
|
|
25657
|
+
/** Remove uploads that have been idle for longer than UPLOAD_EXPIRY_MS. */
|
|
25658
|
+
async cleanupStale() {
|
|
25659
|
+
const now = Date.now();
|
|
25660
|
+
for (const [id, upload] of this.uploads) {
|
|
25661
|
+
if (now - upload.createdAt > UPLOAD_EXPIRY_MS && !upload.completed) {
|
|
25662
|
+
try {
|
|
25663
|
+
await promises.unlink(upload.filePath);
|
|
25664
|
+
} catch {
|
|
25665
|
+
}
|
|
25666
|
+
this.uploads.delete(id);
|
|
25667
|
+
}
|
|
25668
|
+
}
|
|
25669
|
+
}
|
|
25670
|
+
// -----------------------------------------------------------------------
|
|
25671
|
+
// TUS Metadata Parsing
|
|
25672
|
+
// -----------------------------------------------------------------------
|
|
25673
|
+
/**
|
|
25674
|
+
* Parse the `Upload-Metadata` header.
|
|
25675
|
+
*
|
|
25676
|
+
* Format: `key base64value,key2 base64value2`
|
|
25677
|
+
*/
|
|
25678
|
+
parseMetadata(header) {
|
|
25679
|
+
const metadata = {};
|
|
25680
|
+
if (!header) return metadata;
|
|
25681
|
+
for (const pair of header.split(",")) {
|
|
25682
|
+
const trimmed = pair.trim();
|
|
25683
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
25684
|
+
if (spaceIdx === -1) {
|
|
25685
|
+
metadata[trimmed] = "";
|
|
25686
|
+
} else {
|
|
25687
|
+
const key = trimmed.substring(0, spaceIdx);
|
|
25688
|
+
const value = Buffer.from(trimmed.substring(spaceIdx + 1), "base64").toString("utf-8");
|
|
25689
|
+
metadata[key] = value;
|
|
25690
|
+
}
|
|
25691
|
+
}
|
|
25692
|
+
return metadata;
|
|
25693
|
+
}
|
|
25694
|
+
// -----------------------------------------------------------------------
|
|
25695
|
+
// Protocol Endpoints
|
|
25696
|
+
// -----------------------------------------------------------------------
|
|
25697
|
+
/** `OPTIONS /tus` — TUS capability discovery. */
|
|
25698
|
+
options() {
|
|
25699
|
+
return new Response(null, {
|
|
25700
|
+
status: 204,
|
|
25701
|
+
headers: {
|
|
25702
|
+
"Tus-Resumable": "1.0.0",
|
|
25703
|
+
"Tus-Version": "1.0.0",
|
|
25704
|
+
"Tus-Extension": "creation,termination",
|
|
25705
|
+
"Tus-Max-Size": String(MAX_UPLOAD_SIZE)
|
|
25706
|
+
}
|
|
25707
|
+
});
|
|
25708
|
+
}
|
|
25709
|
+
/** `POST /tus` — Create a new upload. */
|
|
25710
|
+
async create(c) {
|
|
25711
|
+
await this.ensureDir();
|
|
25712
|
+
const uploadLengthHeader = c.req.header("Upload-Length");
|
|
25713
|
+
if (!uploadLengthHeader) {
|
|
25714
|
+
return c.json({
|
|
25715
|
+
error: "Upload-Length header is required"
|
|
25716
|
+
}, 400);
|
|
25717
|
+
}
|
|
25718
|
+
const uploadLength = parseInt(uploadLengthHeader, 10);
|
|
25719
|
+
if (Number.isNaN(uploadLength) || uploadLength <= 0) {
|
|
25720
|
+
return c.json({
|
|
25721
|
+
error: "Invalid Upload-Length"
|
|
25722
|
+
}, 400);
|
|
25723
|
+
}
|
|
25724
|
+
if (uploadLength > MAX_UPLOAD_SIZE) {
|
|
25725
|
+
return c.json({
|
|
25726
|
+
error: `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`
|
|
25727
|
+
}, 413);
|
|
25728
|
+
}
|
|
25729
|
+
const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
|
|
25730
|
+
const id = require$$0$5.randomUUID();
|
|
25731
|
+
const filePath = path$3.join(this.tusDir, id);
|
|
25732
|
+
await promises.writeFile(filePath, Buffer.alloc(0));
|
|
25733
|
+
const upload = {
|
|
25734
|
+
id,
|
|
25735
|
+
size: uploadLength,
|
|
25736
|
+
offset: 0,
|
|
25737
|
+
metadata,
|
|
25738
|
+
createdAt: Date.now(),
|
|
25739
|
+
filePath,
|
|
25740
|
+
bucket: metadata.bucket || void 0,
|
|
25741
|
+
key: metadata.key || metadata.filename || void 0,
|
|
25742
|
+
completed: false
|
|
25743
|
+
};
|
|
25744
|
+
this.uploads.set(id, upload);
|
|
25745
|
+
const reqUrl = new URL(c.req.url);
|
|
25746
|
+
const location = `${reqUrl.origin}${reqUrl.pathname}/${id}`;
|
|
25747
|
+
return new Response(null, {
|
|
25748
|
+
status: 201,
|
|
25749
|
+
headers: {
|
|
25750
|
+
Location: location,
|
|
25751
|
+
"Tus-Resumable": "1.0.0",
|
|
25752
|
+
"Upload-Offset": "0"
|
|
25753
|
+
}
|
|
25754
|
+
});
|
|
25755
|
+
}
|
|
25756
|
+
/** `HEAD /tus/:id` — Query upload progress. */
|
|
25757
|
+
head(c, id) {
|
|
25758
|
+
const upload = this.uploads.get(id);
|
|
25759
|
+
if (!upload) {
|
|
25760
|
+
return c.json({
|
|
25761
|
+
error: "Upload not found"
|
|
25762
|
+
}, 404);
|
|
25763
|
+
}
|
|
25764
|
+
return new Response(null, {
|
|
25765
|
+
status: 200,
|
|
25766
|
+
headers: {
|
|
25767
|
+
"Tus-Resumable": "1.0.0",
|
|
25768
|
+
"Upload-Offset": String(upload.offset),
|
|
25769
|
+
"Upload-Length": String(upload.size),
|
|
25770
|
+
"Cache-Control": "no-store"
|
|
25771
|
+
}
|
|
25772
|
+
});
|
|
25773
|
+
}
|
|
25774
|
+
/** `PATCH /tus/:id` — Append data to an upload. */
|
|
25775
|
+
async patch(c, id) {
|
|
25776
|
+
const upload = this.uploads.get(id);
|
|
25777
|
+
if (!upload) {
|
|
25778
|
+
return c.json({
|
|
25779
|
+
error: "Upload not found"
|
|
25780
|
+
}, 404);
|
|
25781
|
+
}
|
|
25782
|
+
if (upload.completed) {
|
|
25783
|
+
return c.json({
|
|
25784
|
+
error: "Upload already completed"
|
|
25785
|
+
}, 400);
|
|
25786
|
+
}
|
|
25787
|
+
const offsetHeader = c.req.header("Upload-Offset");
|
|
25788
|
+
if (!offsetHeader) {
|
|
25789
|
+
return c.json({
|
|
25790
|
+
error: "Upload-Offset header is required"
|
|
25791
|
+
}, 400);
|
|
25792
|
+
}
|
|
25793
|
+
const offset = parseInt(offsetHeader, 10);
|
|
25794
|
+
if (offset !== upload.offset) {
|
|
25795
|
+
return c.json({
|
|
25796
|
+
error: "Offset mismatch"
|
|
25797
|
+
}, 409);
|
|
25798
|
+
}
|
|
25799
|
+
const contentType = c.req.header("Content-Type");
|
|
25800
|
+
if (contentType !== "application/offset+octet-stream") {
|
|
25801
|
+
return c.json({
|
|
25802
|
+
error: "Content-Type must be application/offset+octet-stream"
|
|
25803
|
+
}, 415);
|
|
25804
|
+
}
|
|
25805
|
+
const body = await c.req.arrayBuffer();
|
|
25806
|
+
const chunk = Buffer.from(body);
|
|
25807
|
+
if (upload.offset + chunk.length > upload.size) {
|
|
25808
|
+
return c.json({
|
|
25809
|
+
error: "Chunk exceeds declared Upload-Length"
|
|
25810
|
+
}, 413);
|
|
25811
|
+
}
|
|
25812
|
+
const fh = await promises.open(upload.filePath, "a");
|
|
25813
|
+
try {
|
|
25814
|
+
await fh.write(chunk);
|
|
25815
|
+
} finally {
|
|
25816
|
+
await fh.close();
|
|
25817
|
+
}
|
|
25818
|
+
upload.offset += chunk.length;
|
|
25819
|
+
if (upload.offset >= upload.size) {
|
|
25820
|
+
await this.finalize(upload);
|
|
25821
|
+
}
|
|
25822
|
+
return new Response(null, {
|
|
25823
|
+
status: 204,
|
|
25824
|
+
headers: {
|
|
25825
|
+
"Tus-Resumable": "1.0.0",
|
|
25826
|
+
"Upload-Offset": String(upload.offset)
|
|
25827
|
+
}
|
|
25828
|
+
});
|
|
25829
|
+
}
|
|
25830
|
+
/** `DELETE /tus/:id` — Cancel and remove an upload. */
|
|
25831
|
+
async delete(c, id) {
|
|
25832
|
+
const upload = this.uploads.get(id);
|
|
25833
|
+
if (!upload) {
|
|
25834
|
+
return c.json({
|
|
25835
|
+
error: "Upload not found"
|
|
25836
|
+
}, 404);
|
|
25837
|
+
}
|
|
25838
|
+
try {
|
|
25839
|
+
await promises.unlink(upload.filePath);
|
|
25840
|
+
} catch {
|
|
25841
|
+
}
|
|
25842
|
+
this.uploads.delete(id);
|
|
25843
|
+
return new Response(null, {
|
|
25844
|
+
status: 204,
|
|
25845
|
+
headers: {
|
|
25846
|
+
"Tus-Resumable": "1.0.0"
|
|
25847
|
+
}
|
|
25848
|
+
});
|
|
25849
|
+
}
|
|
25850
|
+
// -----------------------------------------------------------------------
|
|
25851
|
+
// Finalization
|
|
25852
|
+
// -----------------------------------------------------------------------
|
|
25853
|
+
/**
|
|
25854
|
+
* Move a completed upload into the storage controller.
|
|
25855
|
+
*/
|
|
25856
|
+
async finalize(upload) {
|
|
25857
|
+
upload.completed = true;
|
|
25858
|
+
if (!this.storageController) {
|
|
25859
|
+
logger.warn("[TUS] Upload completed but no StorageController configured. Temp file remains:", {
|
|
25860
|
+
filePath: upload.filePath
|
|
25861
|
+
});
|
|
25862
|
+
return;
|
|
25863
|
+
}
|
|
25864
|
+
try {
|
|
25865
|
+
const {
|
|
25866
|
+
readFile: readFile2
|
|
25867
|
+
} = await import("fs/promises");
|
|
25868
|
+
const data = await readFile2(upload.filePath);
|
|
25869
|
+
const fileName = upload.key || upload.metadata.filename || upload.id;
|
|
25870
|
+
const mimeType = upload.metadata.contentType || upload.metadata.filetype || "application/octet-stream";
|
|
25871
|
+
const file = new File([data], fileName, {
|
|
25872
|
+
type: mimeType
|
|
25873
|
+
});
|
|
25874
|
+
await this.storageController.putObject({
|
|
25875
|
+
file,
|
|
25876
|
+
key: fileName,
|
|
25877
|
+
bucket: upload.bucket
|
|
25878
|
+
});
|
|
25879
|
+
try {
|
|
25880
|
+
await promises.unlink(upload.filePath);
|
|
25881
|
+
} catch {
|
|
25882
|
+
}
|
|
25883
|
+
this.uploads.delete(upload.id);
|
|
25884
|
+
logger.info(`[TUS] Upload ${upload.id} finalized → ${fileName}`);
|
|
25885
|
+
} catch (err) {
|
|
25886
|
+
logger.error(`[TUS] Failed to finalize upload ${upload.id}`, {
|
|
25887
|
+
error: err
|
|
25888
|
+
});
|
|
25889
|
+
}
|
|
25890
|
+
}
|
|
25891
|
+
}
|
|
25892
|
+
const transformCache = new TransformCache();
|
|
24036
25893
|
function extractWildcardPath(c) {
|
|
24037
25894
|
const routePath = c.req.routePath;
|
|
24038
25895
|
const prefix = routePath.replace("/*", "");
|
|
@@ -24096,6 +25953,7 @@ ${credentialScope}
|
|
|
24096
25953
|
throw ApiError.notFound("File not found");
|
|
24097
25954
|
}
|
|
24098
25955
|
const filePath = decodeURIComponent(rawPath);
|
|
25956
|
+
const transformOpts = parseTransformOptions(c.req.query());
|
|
24099
25957
|
if (controller.getType() === "local") {
|
|
24100
25958
|
const localController = controller;
|
|
24101
25959
|
const {
|
|
@@ -24115,8 +25973,19 @@ ${credentialScope}
|
|
|
24115
25973
|
} catch {
|
|
24116
25974
|
}
|
|
24117
25975
|
}
|
|
24118
|
-
c.header("Content-Type", contentType);
|
|
24119
25976
|
const fileContent = fs__namespace.readFileSync(absolutePath);
|
|
25977
|
+
if (transformOpts && isTransformableImage(contentType)) {
|
|
25978
|
+
const cacheKey = transformCache.buildKey(filePath, transformOpts);
|
|
25979
|
+
let cached = transformCache.get(cacheKey);
|
|
25980
|
+
if (!cached) {
|
|
25981
|
+
cached = await transformImage(Buffer.from(fileContent), transformOpts);
|
|
25982
|
+
transformCache.set(cacheKey, cached.data, cached.contentType);
|
|
25983
|
+
}
|
|
25984
|
+
c.header("Content-Type", cached.contentType);
|
|
25985
|
+
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
25986
|
+
return c.body(new Uint8Array(cached.data));
|
|
25987
|
+
}
|
|
25988
|
+
c.header("Content-Type", contentType);
|
|
24120
25989
|
return c.body(new Uint8Array(fileContent));
|
|
24121
25990
|
}
|
|
24122
25991
|
const {
|
|
@@ -24127,7 +25996,20 @@ ${credentialScope}
|
|
|
24127
25996
|
if (!fileObject) {
|
|
24128
25997
|
throw ApiError.notFound("File not found");
|
|
24129
25998
|
}
|
|
24130
|
-
|
|
25999
|
+
const remoteContentType = fileObject.type || "application/octet-stream";
|
|
26000
|
+
if (transformOpts && isTransformableImage(remoteContentType)) {
|
|
26001
|
+
const cacheKey = transformCache.buildKey(filePath, transformOpts);
|
|
26002
|
+
let cached = transformCache.get(cacheKey);
|
|
26003
|
+
if (!cached) {
|
|
26004
|
+
const buf2 = Buffer.from(await fileObject.arrayBuffer());
|
|
26005
|
+
cached = await transformImage(buf2, transformOpts);
|
|
26006
|
+
transformCache.set(cacheKey, cached.data, cached.contentType);
|
|
26007
|
+
}
|
|
26008
|
+
c.header("Content-Type", cached.contentType);
|
|
26009
|
+
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
26010
|
+
return c.body(new Uint8Array(cached.data));
|
|
26011
|
+
}
|
|
26012
|
+
c.header("Content-Type", remoteContentType);
|
|
24131
26013
|
c.header("Cache-Control", "public, max-age=3600, immutable");
|
|
24132
26014
|
const buf = await fileObject.arrayBuffer();
|
|
24133
26015
|
return c.body(new Uint8Array(buf));
|
|
@@ -24223,6 +26105,14 @@ ${credentialScope}
|
|
|
24223
26105
|
message: "Folder created"
|
|
24224
26106
|
}, 201);
|
|
24225
26107
|
});
|
|
26108
|
+
const tusBaseDir = controller.getType() === "local" ? controller.getBasePath() : process.env.STORAGE_PATH || "./uploads";
|
|
26109
|
+
const tusHandler = new TusHandler(tusBaseDir, controller);
|
|
26110
|
+
tusHandler.startCleanup();
|
|
26111
|
+
router.options("/tus", (_c2) => tusHandler.options());
|
|
26112
|
+
router.post("/tus", writeAuthMiddleware, async (c) => tusHandler.create(c));
|
|
26113
|
+
router.get("/tus/:id", readAuthMiddleware, (c) => tusHandler.head(c, c.req.param("id")));
|
|
26114
|
+
router.patch("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.patch(c, c.req.param("id")));
|
|
26115
|
+
router.delete("/tus/:id", writeAuthMiddleware, async (c) => tusHandler.delete(c, c.req.param("id")));
|
|
24226
26116
|
return router;
|
|
24227
26117
|
}
|
|
24228
26118
|
const DEFAULT_STORAGE_ID = "(default)";
|
|
@@ -24446,6 +26336,13 @@ ${credentialScope}
|
|
|
24446
26336
|
} catch (e) {
|
|
24447
26337
|
}
|
|
24448
26338
|
}
|
|
26339
|
+
const getErrorField = (obj, field) => {
|
|
26340
|
+
const err = obj?.error;
|
|
26341
|
+
if (err && typeof err === "object" && err !== null && field in err) {
|
|
26342
|
+
return err[field];
|
|
26343
|
+
}
|
|
26344
|
+
return obj?.[field];
|
|
26345
|
+
};
|
|
24449
26346
|
if (res.status === 401 && onUnauthorizedHandler) {
|
|
24450
26347
|
const retried = await onUnauthorizedHandler();
|
|
24451
26348
|
if (retried) {
|
|
@@ -24479,7 +26376,7 @@ ${credentialScope}
|
|
|
24479
26376
|
const method = init?.method || "GET";
|
|
24480
26377
|
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
26378
|
}
|
|
24482
|
-
throw new RebaseApiError(retryRes.status, retryBody
|
|
26379
|
+
throw new RebaseApiError(retryRes.status, String(getErrorField(retryBody, "message") || fallbackMessage || `Request failed with status ${retryRes.status}`), getErrorField(retryBody, "code"), getErrorField(retryBody, "details"));
|
|
24483
26380
|
}
|
|
24484
26381
|
return retryBody;
|
|
24485
26382
|
}
|
|
@@ -24490,7 +26387,7 @@ ${credentialScope}
|
|
|
24490
26387
|
const method = init?.method || "GET";
|
|
24491
26388
|
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
26389
|
}
|
|
24493
|
-
throw new RebaseApiError(res.status, body
|
|
26390
|
+
throw new RebaseApiError(res.status, String(getErrorField(body, "message") || fallbackMessage || `Request failed with status ${res.status}`), getErrorField(body, "code"), getErrorField(body, "details"));
|
|
24494
26391
|
}
|
|
24495
26392
|
return body;
|
|
24496
26393
|
}
|
|
@@ -25746,7 +27643,7 @@ ${credentialScope}
|
|
|
25746
27643
|
const http = require$$0$6;
|
|
25747
27644
|
const https = require$$1;
|
|
25748
27645
|
const urllib$2 = require$$0$2;
|
|
25749
|
-
const zlib = require$$
|
|
27646
|
+
const zlib = require$$3;
|
|
25750
27647
|
const PassThrough$3 = require$$0$4.PassThrough;
|
|
25751
27648
|
const Cookies = cookies;
|
|
25752
27649
|
const packageData$7 = require$$9;
|
|
@@ -25981,9 +27878,9 @@ ${credentialScope}
|
|
|
25981
27878
|
const util2 = require$$5;
|
|
25982
27879
|
const fs2 = fs$4;
|
|
25983
27880
|
const nmfetch2 = fetchExports;
|
|
25984
|
-
const dns2 = require$$
|
|
27881
|
+
const dns2 = require$$4$1;
|
|
25985
27882
|
const net2 = require$$0$7;
|
|
25986
|
-
const os2 = require$$1$
|
|
27883
|
+
const os2 = require$$1$3;
|
|
25987
27884
|
const DNS_TTL = 5 * 60 * 1e3;
|
|
25988
27885
|
let networkInterfaces;
|
|
25989
27886
|
try {
|
|
@@ -32129,7 +34026,7 @@ ${credentialScope}
|
|
|
32129
34026
|
const packageData$6 = require$$9;
|
|
32130
34027
|
const MailMessage = mailMessage;
|
|
32131
34028
|
const net$1 = require$$0$7;
|
|
32132
|
-
const dns = require$$
|
|
34029
|
+
const dns = require$$4$1;
|
|
32133
34030
|
const crypto$3 = require$$0$5;
|
|
32134
34031
|
class Mail extends EventEmitter$5 {
|
|
32135
34032
|
constructor(transporter, options2, defaults) {
|
|
@@ -32565,7 +34462,7 @@ ${credentialScope}
|
|
|
32565
34462
|
const EventEmitter$4 = require$$0$9.EventEmitter;
|
|
32566
34463
|
const net = require$$0$7;
|
|
32567
34464
|
const tls = require$$1$1;
|
|
32568
|
-
const os = require$$1$
|
|
34465
|
+
const os = require$$1$3;
|
|
32569
34466
|
const crypto$2 = require$$0$5;
|
|
32570
34467
|
const DataStream = dataStream;
|
|
32571
34468
|
const PassThrough = require$$0$4.PassThrough;
|
|
@@ -36753,7 +38650,7 @@ ${credentialScope}
|
|
|
36753
38650
|
oauthProviders,
|
|
36754
38651
|
serviceKey,
|
|
36755
38652
|
hooks: config.hooks,
|
|
36756
|
-
|
|
38653
|
+
authHooks: safeAuthConfig.hooks
|
|
36757
38654
|
});
|
|
36758
38655
|
}
|
|
36759
38656
|
logger.info("Authentication initialized");
|
|
@@ -36898,7 +38795,7 @@ ${credentialScope}
|
|
|
36898
38795
|
oauthProviders,
|
|
36899
38796
|
serviceKey,
|
|
36900
38797
|
hooks: config.hooks,
|
|
36901
|
-
|
|
38798
|
+
authHooks: safeAuthConfig.hooks
|
|
36902
38799
|
});
|
|
36903
38800
|
}
|
|
36904
38801
|
if (authAdapter.createAuthRoutes) {
|
|
@@ -36920,6 +38817,21 @@ ${credentialScope}
|
|
|
36920
38817
|
}
|
|
36921
38818
|
}
|
|
36922
38819
|
}
|
|
38820
|
+
let apiKeyStore;
|
|
38821
|
+
const apiKeyStoreResult = createApiKeyStore(defaultDriver);
|
|
38822
|
+
if (apiKeyStoreResult) {
|
|
38823
|
+
apiKeyStore = apiKeyStoreResult;
|
|
38824
|
+
await apiKeyStore.ensureTable();
|
|
38825
|
+
logger.info("Service API Keys initialized");
|
|
38826
|
+
const apiKeyRoutes = createApiKeyRoutes({
|
|
38827
|
+
store: apiKeyStore,
|
|
38828
|
+
serviceKey
|
|
38829
|
+
});
|
|
38830
|
+
config.app.route(`${basePath}/admin/api-keys`, apiKeyRoutes);
|
|
38831
|
+
logger.info("API key admin routes mounted", {
|
|
38832
|
+
path: `${basePath}/admin/api-keys`
|
|
38833
|
+
});
|
|
38834
|
+
}
|
|
36923
38835
|
if (config.collectionsDir) {
|
|
36924
38836
|
if (process.env.NODE_ENV !== "production") {
|
|
36925
38837
|
const {
|
|
@@ -36970,15 +38882,20 @@ ${credentialScope}
|
|
|
36970
38882
|
dataRouter.use("/*", createAdapterAuthMiddleware({
|
|
36971
38883
|
adapter: authAdapter,
|
|
36972
38884
|
driver: defaultDriver,
|
|
36973
|
-
requireAuth: dataRequireAuth
|
|
38885
|
+
requireAuth: dataRequireAuth,
|
|
38886
|
+
apiKeyStore
|
|
36974
38887
|
}));
|
|
36975
38888
|
} else {
|
|
36976
38889
|
dataRouter.use("/*", createAuthMiddleware({
|
|
36977
38890
|
driver: defaultDriver,
|
|
36978
38891
|
requireAuth: dataRequireAuth,
|
|
36979
|
-
serviceKey
|
|
38892
|
+
serviceKey,
|
|
38893
|
+
apiKeyStore
|
|
36980
38894
|
}));
|
|
36981
38895
|
}
|
|
38896
|
+
if (apiKeyStore) {
|
|
38897
|
+
dataRouter.use("/*", createApiKeyRateLimiter());
|
|
38898
|
+
}
|
|
36982
38899
|
if (historyConfigResult && historyConfigResult.historyService) {
|
|
36983
38900
|
const historyRoutes = createHistoryRoutes({
|
|
36984
38901
|
historyService: historyConfigResult.historyService,
|
|
@@ -37072,13 +38989,15 @@ ${credentialScope}
|
|
|
37072
38989
|
functionsRouter.use("/*", createAdapterAuthMiddleware({
|
|
37073
38990
|
adapter: authAdapter,
|
|
37074
38991
|
driver: defaultDriver,
|
|
37075
|
-
requireAuth: functionsRequireAuth
|
|
38992
|
+
requireAuth: functionsRequireAuth,
|
|
38993
|
+
apiKeyStore
|
|
37076
38994
|
}));
|
|
37077
38995
|
} else {
|
|
37078
38996
|
functionsRouter.use("/*", createAuthMiddleware({
|
|
37079
38997
|
driver: defaultDriver,
|
|
37080
38998
|
requireAuth: functionsRequireAuth,
|
|
37081
|
-
serviceKey
|
|
38999
|
+
serviceKey,
|
|
39000
|
+
apiKeyStore
|
|
37082
39001
|
}));
|
|
37083
39002
|
}
|
|
37084
39003
|
const fnRoutes = createFunctionRoutes2(loadedFunctions);
|
|
@@ -47077,7 +48996,7 @@ spurious results.`);
|
|
|
47077
48996
|
return "undefined";
|
|
47078
48997
|
}
|
|
47079
48998
|
if (typeof obj === "string") {
|
|
47080
|
-
return
|
|
48999
|
+
return JSON.stringify(obj);
|
|
47081
49000
|
}
|
|
47082
49001
|
if (typeof obj === "number" || typeof obj === "boolean") {
|
|
47083
49002
|
return String(obj);
|
|
@@ -47108,7 +49027,7 @@ ${indent2}]`;
|
|
|
47108
49027
|
const kind = init.getKind();
|
|
47109
49028
|
const isCode = kind === tsMorph.SyntaxKind.ArrowFunction || kind === tsMorph.SyntaxKind.FunctionExpression || kind === tsMorph.SyntaxKind.Identifier || kind === tsMorph.SyntaxKind.CallExpression || kind === tsMorph.SyntaxKind.JsxElement;
|
|
47110
49029
|
if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
|
|
47111
|
-
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 :
|
|
49030
|
+
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name2) ? name2 : JSON.stringify(name2);
|
|
47112
49031
|
preservedProps.push(`${keyStr}: ${init.getText()}`);
|
|
47113
49032
|
}
|
|
47114
49033
|
}
|
|
@@ -47118,7 +49037,7 @@ ${indent2}]`;
|
|
|
47118
49037
|
}
|
|
47119
49038
|
if (keys2.length === 0 && preservedProps.length === 0) return "{}";
|
|
47120
49039
|
const props = keys2.map((key) => {
|
|
47121
|
-
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key :
|
|
49040
|
+
const keyStr = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
47122
49041
|
let childAstNode;
|
|
47123
49042
|
if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
|
|
47124
49043
|
const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
|
|
@@ -47160,7 +49079,7 @@ ${indent2}}`;
|
|
|
47160
49079
|
}
|
|
47161
49080
|
} else {
|
|
47162
49081
|
propsObj.addPropertyAssignment({
|
|
47163
|
-
name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey :
|
|
49082
|
+
name: /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyKey) ? propertyKey : JSON.stringify(propertyKey),
|
|
47164
49083
|
initializer: newInitializer
|
|
47165
49084
|
});
|
|
47166
49085
|
}
|
|
@@ -48231,7 +50150,7 @@ export default ${safeId}Collection;
|
|
|
48231
50150
|
const mod = await dynamicImport(fileUrl);
|
|
48232
50151
|
const exported = mod.default;
|
|
48233
50152
|
if (!exported) {
|
|
48234
|
-
|
|
50153
|
+
logger.warn(`[functions] ${file}: no default export. Skipping.`);
|
|
48235
50154
|
continue;
|
|
48236
50155
|
}
|
|
48237
50156
|
if (isHonoLike(exported)) {
|
|
@@ -48240,7 +50159,7 @@ export default ${safeId}Collection;
|
|
|
48240
50159
|
name: name2,
|
|
48241
50160
|
app: exported
|
|
48242
50161
|
});
|
|
48243
|
-
|
|
50162
|
+
logger.info(`⚡ Loaded function route: ${name2}`);
|
|
48244
50163
|
continue;
|
|
48245
50164
|
}
|
|
48246
50165
|
if (typeof exported === "function") {
|
|
@@ -48251,20 +50170,20 @@ export default ${safeId}Collection;
|
|
|
48251
50170
|
name: name2,
|
|
48252
50171
|
app: result
|
|
48253
50172
|
});
|
|
48254
|
-
|
|
50173
|
+
logger.info(`⚡ Loaded function route: ${name2}`);
|
|
48255
50174
|
continue;
|
|
48256
50175
|
}
|
|
48257
50176
|
}
|
|
48258
50177
|
const exportType = typeof exported;
|
|
48259
50178
|
const keys2 = exported && typeof exported === "object" ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ") : "N/A";
|
|
48260
|
-
|
|
50179
|
+
logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
|
|
48261
50180
|
export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
|
|
48262
50181
|
prototype methods: ${keys2}
|
|
48263
50182
|
Hint: ensure the function exports a Hono app created with the same hono version as the server.
|
|
48264
50183
|
The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
|
|
48265
50184
|
} catch (err) {
|
|
48266
50185
|
const message = err instanceof Error ? err.message : String(err);
|
|
48267
|
-
|
|
50186
|
+
logger.error(`[functions] Failed to load ${file}: ${message}`);
|
|
48268
50187
|
}
|
|
48269
50188
|
}
|
|
48270
50189
|
}
|
|
@@ -48313,12 +50232,12 @@ export default ${safeId}Collection;
|
|
|
48313
50232
|
const mod = await dynamicImport(fileUrl);
|
|
48314
50233
|
const exported = mod.default;
|
|
48315
50234
|
if (!exported || typeof exported !== "object") {
|
|
48316
|
-
|
|
50235
|
+
logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
|
|
48317
50236
|
continue;
|
|
48318
50237
|
}
|
|
48319
50238
|
const def = exported;
|
|
48320
50239
|
if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
|
|
48321
|
-
|
|
50240
|
+
logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
|
|
48322
50241
|
continue;
|
|
48323
50242
|
}
|
|
48324
50243
|
const id = path__namespace.basename(file, path__namespace.extname(file));
|
|
@@ -48334,10 +50253,10 @@ export default ${safeId}Collection;
|
|
|
48334
50253
|
id,
|
|
48335
50254
|
definition
|
|
48336
50255
|
});
|
|
48337
|
-
|
|
50256
|
+
logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
|
|
48338
50257
|
} catch (err) {
|
|
48339
50258
|
const message = err instanceof Error ? err.message : String(err);
|
|
48340
|
-
|
|
50259
|
+
logger.error(`[cron] Failed to load ${file}: ${message}`);
|
|
48341
50260
|
}
|
|
48342
50261
|
}
|
|
48343
50262
|
}
|
|
@@ -48492,12 +50411,12 @@ export default ${safeId}Collection;
|
|
|
48492
50411
|
for (const loaded of loadedJobs) {
|
|
48493
50412
|
const validation = validateCronExpression(loaded.definition.schedule);
|
|
48494
50413
|
if (!validation.valid) {
|
|
48495
|
-
|
|
50414
|
+
logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
|
|
48496
50415
|
continue;
|
|
48497
50416
|
}
|
|
48498
50417
|
const existing = this.jobs.get(loaded.id);
|
|
48499
50418
|
if (existing) {
|
|
48500
|
-
|
|
50419
|
+
logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
|
|
48501
50420
|
this.stopJob(loaded.id);
|
|
48502
50421
|
}
|
|
48503
50422
|
const enabled = loaded.definition.enabled !== false;
|
|
@@ -48535,7 +50454,9 @@ export default ${safeId}Collection;
|
|
|
48535
50454
|
}
|
|
48536
50455
|
}
|
|
48537
50456
|
}).catch((err) => {
|
|
48538
|
-
|
|
50457
|
+
logger.warn("[cron] Failed to seed job stats from database", {
|
|
50458
|
+
error: err
|
|
50459
|
+
});
|
|
48539
50460
|
});
|
|
48540
50461
|
}
|
|
48541
50462
|
for (const [id, job] of this.jobs) {
|
|
@@ -48543,7 +50464,7 @@ export default ${safeId}Collection;
|
|
|
48543
50464
|
this.scheduleNext(id);
|
|
48544
50465
|
}
|
|
48545
50466
|
}
|
|
48546
|
-
|
|
50467
|
+
logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
|
|
48547
50468
|
}
|
|
48548
50469
|
/**
|
|
48549
50470
|
* Stop the scheduler and clear all timers.
|
|
@@ -48617,7 +50538,7 @@ export default ${safeId}Collection;
|
|
|
48617
50538
|
const job = this.jobs.get(id);
|
|
48618
50539
|
if (!job) return void 0;
|
|
48619
50540
|
if (job.executing) {
|
|
48620
|
-
|
|
50541
|
+
logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
|
|
48621
50542
|
const logEntry = {
|
|
48622
50543
|
jobId: id,
|
|
48623
50544
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -48661,7 +50582,7 @@ export default ${safeId}Collection;
|
|
|
48661
50582
|
const timer = setTimeout(async () => {
|
|
48662
50583
|
if (!job.enabled || !this.started) return;
|
|
48663
50584
|
if (job.executing) {
|
|
48664
|
-
|
|
50585
|
+
logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
|
|
48665
50586
|
this.scheduleNext(id);
|
|
48666
50587
|
return;
|
|
48667
50588
|
}
|
|
@@ -48675,7 +50596,9 @@ export default ${safeId}Collection;
|
|
|
48675
50596
|
}
|
|
48676
50597
|
job.timerId = timer;
|
|
48677
50598
|
} catch (err) {
|
|
48678
|
-
|
|
50599
|
+
logger.error(`[cron] Failed to schedule "${id}"`, {
|
|
50600
|
+
error: err
|
|
50601
|
+
});
|
|
48679
50602
|
job.state = "error";
|
|
48680
50603
|
job.lastError = err instanceof Error ? err.message : String(err);
|
|
48681
50604
|
}
|
|
@@ -48760,13 +50683,15 @@ export default ${safeId}Collection;
|
|
|
48760
50683
|
}
|
|
48761
50684
|
if (this.store) {
|
|
48762
50685
|
this.store.insertLog(logEntry).catch((persistErr) => {
|
|
48763
|
-
|
|
50686
|
+
logger.error(`[cron] Failed to persist log for "${job.id}"`, {
|
|
50687
|
+
error: persistErr
|
|
50688
|
+
});
|
|
48764
50689
|
});
|
|
48765
50690
|
}
|
|
48766
50691
|
if (success) {
|
|
48767
|
-
|
|
50692
|
+
logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
|
|
48768
50693
|
} else {
|
|
48769
|
-
|
|
50694
|
+
logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
|
|
48770
50695
|
}
|
|
48771
50696
|
return logEntry;
|
|
48772
50697
|
}
|
|
@@ -48884,7 +50809,7 @@ export default ${safeId}Collection;
|
|
|
48884
50809
|
function createCronStore(driver) {
|
|
48885
50810
|
const admin = driver.admin;
|
|
48886
50811
|
if (!isSQLAdmin(admin)) {
|
|
48887
|
-
|
|
50812
|
+
logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
|
|
48888
50813
|
return void 0;
|
|
48889
50814
|
}
|
|
48890
50815
|
const exec = admin.executeSql.bind(admin);
|
|
@@ -48910,10 +50835,12 @@ export default ${safeId}Collection;
|
|
|
48910
50835
|
CREATE INDEX IF NOT EXISTS idx_cron_logs_job
|
|
48911
50836
|
ON ${TABLE}(job_id, started_at DESC)
|
|
48912
50837
|
`);
|
|
48913
|
-
|
|
50838
|
+
logger.info("✅ Cron logs table ready");
|
|
48914
50839
|
} catch (err) {
|
|
48915
|
-
|
|
48916
|
-
|
|
50840
|
+
logger.error("❌ Failed to create cron logs table", {
|
|
50841
|
+
error: err
|
|
50842
|
+
});
|
|
50843
|
+
logger.warn("⚠️ Continuing without cron log persistence.");
|
|
48917
50844
|
}
|
|
48918
50845
|
},
|
|
48919
50846
|
async insertLog(entry) {
|
|
@@ -48936,7 +50863,9 @@ export default ${safeId}Collection;
|
|
|
48936
50863
|
)
|
|
48937
50864
|
`);
|
|
48938
50865
|
} catch (err) {
|
|
48939
|
-
|
|
50866
|
+
logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
|
|
50867
|
+
error: err
|
|
50868
|
+
});
|
|
48940
50869
|
}
|
|
48941
50870
|
},
|
|
48942
50871
|
async fetchLogs(jobId, limit = 50) {
|
|
@@ -48950,7 +50879,9 @@ export default ${safeId}Collection;
|
|
|
48950
50879
|
`);
|
|
48951
50880
|
return rows.map(rowToLogEntry);
|
|
48952
50881
|
} catch (err) {
|
|
48953
|
-
|
|
50882
|
+
logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
|
|
50883
|
+
error: err
|
|
50884
|
+
});
|
|
48954
50885
|
return [];
|
|
48955
50886
|
}
|
|
48956
50887
|
},
|
|
@@ -48974,7 +50905,9 @@ export default ${safeId}Collection;
|
|
|
48974
50905
|
});
|
|
48975
50906
|
}
|
|
48976
50907
|
} catch (err) {
|
|
48977
|
-
|
|
50908
|
+
logger.error("[cron-store] Failed to fetch job stats", {
|
|
50909
|
+
error: err
|
|
50910
|
+
});
|
|
48978
50911
|
}
|
|
48979
50912
|
return stats;
|
|
48980
50913
|
}
|
|
@@ -49005,8 +50938,8 @@ export default ${safeId}Collection;
|
|
|
49005
50938
|
indexFile = "index.html"
|
|
49006
50939
|
} = config;
|
|
49007
50940
|
if (!fs__namespace.existsSync(frontendPath)) {
|
|
49008
|
-
|
|
49009
|
-
|
|
50941
|
+
logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
|
|
50942
|
+
logger.warn(" SPA serving is disabled. Build your frontend first.");
|
|
49010
50943
|
return;
|
|
49011
50944
|
}
|
|
49012
50945
|
app.use("/*", serveStatic.serveStatic({
|
|
@@ -49019,13 +50952,13 @@ export default ${safeId}Collection;
|
|
|
49019
50952
|
}
|
|
49020
50953
|
const indexPath = path__namespace.join(frontendPath, indexFile);
|
|
49021
50954
|
if (!fs__namespace.existsSync(indexPath)) {
|
|
49022
|
-
|
|
50955
|
+
logger.warn(`⚠️ Index file not found: ${indexPath}`);
|
|
49023
50956
|
return next();
|
|
49024
50957
|
}
|
|
49025
50958
|
const html = fs__namespace.readFileSync(indexPath, "utf-8");
|
|
49026
50959
|
return c.html(html);
|
|
49027
50960
|
});
|
|
49028
|
-
|
|
50961
|
+
logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
|
|
49029
50962
|
}
|
|
49030
50963
|
const MAX_PORT_ATTEMPTS = 20;
|
|
49031
50964
|
const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
@@ -49033,6 +50966,19 @@ export default ${safeId}Collection;
|
|
|
49033
50966
|
const host = options2?.host ?? "0.0.0.0";
|
|
49034
50967
|
const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
|
|
49035
50968
|
const portFileDir = options2?.portFileDir;
|
|
50969
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
50970
|
+
if (isProd) {
|
|
50971
|
+
return new Promise((resolve, reject) => {
|
|
50972
|
+
const onError = (err) => {
|
|
50973
|
+
reject(err);
|
|
50974
|
+
};
|
|
50975
|
+
server.once("error", onError);
|
|
50976
|
+
server.listen(startPort, host, () => {
|
|
50977
|
+
server.removeListener("error", onError);
|
|
50978
|
+
resolve(startPort);
|
|
50979
|
+
});
|
|
50980
|
+
});
|
|
50981
|
+
}
|
|
49036
50982
|
let affinityPort = null;
|
|
49037
50983
|
if (portFileDir) {
|
|
49038
50984
|
try {
|
|
@@ -49175,6 +51121,8 @@ export default ${safeId}Collection;
|
|
|
49175
51121
|
DB_POOL_MAX: stringType().default("20").transform(Number),
|
|
49176
51122
|
DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
|
|
49177
51123
|
DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
|
|
51124
|
+
DATABASE_DIRECT_URL: stringType().url().optional(),
|
|
51125
|
+
DATABASE_READ_URL: stringType().url().optional(),
|
|
49178
51126
|
FORCE_LOCAL_STORAGE: optionalBoolString,
|
|
49179
51127
|
STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
|
|
49180
51128
|
STORAGE_PATH: stringType().optional(),
|
|
@@ -49250,8 +51198,11 @@ export default ${safeId}Collection;
|
|
|
49250
51198
|
exports2.RestApiGenerator = RestApiGenerator;
|
|
49251
51199
|
exports2.S3StorageController = S3StorageController;
|
|
49252
51200
|
exports2.SMTPEmailService = SMTPEmailService;
|
|
51201
|
+
exports2.TransformCache = TransformCache;
|
|
51202
|
+
exports2.TusHandler = TusHandler;
|
|
49253
51203
|
exports2._resetRebaseMock = _resetRebaseMock;
|
|
49254
51204
|
exports2._setRebaseMock = _setRebaseMock;
|
|
51205
|
+
exports2.apiKeyKeyGenerator = apiKeyKeyGenerator;
|
|
49255
51206
|
exports2.authJwt = authJwt;
|
|
49256
51207
|
exports2.authRoles = authRoles;
|
|
49257
51208
|
exports2.authUid = authUid;
|
|
@@ -49260,6 +51211,9 @@ export default ${safeId}Collection;
|
|
|
49260
51211
|
exports2.configureLogLevel = configureLogLevel;
|
|
49261
51212
|
exports2.createAdapterAuthMiddleware = createAdapterAuthMiddleware;
|
|
49262
51213
|
exports2.createAdminRoutes = createAdminRoutes;
|
|
51214
|
+
exports2.createApiKeyRateLimiter = createApiKeyRateLimiter;
|
|
51215
|
+
exports2.createApiKeyRoutes = createApiKeyRoutes;
|
|
51216
|
+
exports2.createApiKeyStore = createApiKeyStore;
|
|
49263
51217
|
exports2.createAppleProvider = createAppleProvider;
|
|
49264
51218
|
exports2.createAuthMiddleware = createAuthMiddleware;
|
|
49265
51219
|
exports2.createAuthRoutes = createAuthRoutes;
|
|
@@ -49297,27 +51251,35 @@ export default ${safeId}Collection;
|
|
|
49297
51251
|
exports2.getWelcomeEmailTemplate = getWelcomeEmailTemplate;
|
|
49298
51252
|
exports2.hashPassword = hashPassword;
|
|
49299
51253
|
exports2.hashRefreshToken = hashRefreshToken;
|
|
51254
|
+
exports2.httpMethodToOperation = httpMethodToOperation;
|
|
49300
51255
|
exports2.initializeRebaseBackend = initializeRebaseBackend;
|
|
51256
|
+
exports2.isApiKeyToken = isApiKeyToken;
|
|
49301
51257
|
exports2.isAuthAdapter = isAuthAdapter;
|
|
49302
51258
|
exports2.isDatabaseAdapter = isDatabaseAdapter;
|
|
51259
|
+
exports2.isOperationAllowed = isOperationAllowed;
|
|
51260
|
+
exports2.isTransformableImage = isTransformableImage;
|
|
49303
51261
|
exports2.listenWithPortRetry = listenWithPortRetry;
|
|
49304
51262
|
exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
|
|
49305
51263
|
exports2.loadEnv = loadEnv;
|
|
49306
51264
|
exports2.loadFunctionsFromDirectory = loadFunctionsFromDirectory;
|
|
49307
51265
|
exports2.logger = logger;
|
|
49308
51266
|
exports2.optionalAuth = optionalAuth;
|
|
51267
|
+
exports2.parseTransformOptions = parseTransformOptions;
|
|
49309
51268
|
exports2.rebase = rebase;
|
|
49310
51269
|
exports2.requestLogger = requestLogger;
|
|
49311
51270
|
exports2.requireAdmin = requireAdmin;
|
|
49312
51271
|
exports2.requireAuth = requireAuth;
|
|
49313
51272
|
exports2.resetConsole = resetConsole;
|
|
49314
|
-
exports2.
|
|
51273
|
+
exports2.resolveAuthHooks = resolveAuthHooks;
|
|
49315
51274
|
exports2.serveSPA = serveSPA;
|
|
49316
51275
|
exports2.strictAuthLimiter = strictAuthLimiter;
|
|
51276
|
+
exports2.transformImage = transformImage;
|
|
51277
|
+
exports2.validateApiKey = validateApiKey;
|
|
49317
51278
|
exports2.validateCronExpression = validateCronExpression;
|
|
49318
51279
|
exports2.validatePasswordStrength = validatePasswordStrength;
|
|
49319
51280
|
exports2.verifyAccessToken = verifyAccessToken;
|
|
49320
51281
|
exports2.verifyPassword = verifyPassword;
|
|
51282
|
+
exports2.z = z;
|
|
49321
51283
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
49322
51284
|
});
|
|
49323
51285
|
//# sourceMappingURL=index.umd.js.map
|