@rebasepro/server-core 0.2.1 → 0.2.4
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/data/query_builder.d.ts +51 -0
- package/dist/common/src/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 +2340 -370
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2329 -355
- package/dist/index.umd.js.map +1 -1
- package/dist/server-core/src/api/logs-routes.d.ts +37 -0
- package/dist/server-core/src/api/rest/api-generator.d.ts +6 -0
- package/dist/server-core/src/api/types.d.ts +6 -1
- package/dist/server-core/src/auth/adapter-middleware.d.ts +7 -3
- package/dist/server-core/src/auth/admin-routes.d.ts +3 -3
- package/dist/server-core/src/auth/api-keys/api-key-middleware.d.ts +39 -0
- package/dist/server-core/src/auth/api-keys/api-key-permission-guard.d.ts +32 -0
- package/dist/server-core/src/auth/api-keys/api-key-routes.d.ts +20 -0
- package/dist/server-core/src/auth/api-keys/api-key-store.d.ts +35 -0
- package/dist/server-core/src/auth/api-keys/api-key-types.d.ts +88 -0
- package/dist/server-core/src/auth/api-keys/index.d.ts +17 -0
- package/dist/server-core/src/auth/{auth-overrides.d.ts → auth-hooks.d.ts} +69 -11
- package/dist/server-core/src/auth/builtin-auth-adapter.d.ts +3 -3
- package/dist/server-core/src/auth/index.d.ts +5 -3
- package/dist/server-core/src/auth/interfaces.d.ts +93 -3
- package/dist/server-core/src/auth/jwt.d.ts +3 -1
- package/dist/server-core/src/auth/mfa.d.ts +49 -0
- package/dist/server-core/src/auth/middleware.d.ts +7 -0
- package/dist/server-core/src/auth/rate-limiter.d.ts +19 -0
- package/dist/server-core/src/auth/routes.d.ts +3 -3
- package/dist/server-core/src/env.d.ts +6 -0
- package/dist/server-core/src/index.d.ts +1 -0
- package/dist/server-core/src/init.d.ts +3 -3
- package/dist/server-core/src/services/webhook-service.d.ts +29 -0
- package/dist/server-core/src/storage/image-transform.d.ts +48 -0
- package/dist/server-core/src/storage/index.d.ts +3 -0
- package/dist/server-core/src/storage/tus-handler.d.ts +51 -0
- package/dist/types/src/controllers/auth.d.ts +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.d.ts +21 -0
- 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/jest.config.cjs +4 -1
- package/package.json +9 -7
- 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];
|
|
@@ -1156,42 +1135,6 @@
|
|
|
1156
1135
|
});
|
|
1157
1136
|
}
|
|
1158
1137
|
}
|
|
1159
|
-
function getSubcollections(collection) {
|
|
1160
|
-
if (collection.childCollections) {
|
|
1161
|
-
return collection.childCollections() ?? [];
|
|
1162
|
-
}
|
|
1163
|
-
if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
1164
|
-
return collection.subcollections() ?? [];
|
|
1165
|
-
}
|
|
1166
|
-
if (getDataSourceCapabilities(collection.driver).supportsRelations && collection.relations) {
|
|
1167
|
-
const manyRelations = collection.relations.filter((r) => r.cardinality === "many");
|
|
1168
|
-
return manyRelations.map((r) => {
|
|
1169
|
-
const target = r.target();
|
|
1170
|
-
if (!target) return void 0;
|
|
1171
|
-
const relationKey = r.relationName || target.slug;
|
|
1172
|
-
let customName;
|
|
1173
|
-
if (collection.properties) {
|
|
1174
|
-
const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
|
|
1175
|
-
if (prop && prop[1].name) {
|
|
1176
|
-
customName = prop[1].name;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
const baseOverrides = {
|
|
1180
|
-
slug: relationKey
|
|
1181
|
-
};
|
|
1182
|
-
if (customName) {
|
|
1183
|
-
baseOverrides.name = customName;
|
|
1184
|
-
baseOverrides.singularName = customName;
|
|
1185
|
-
}
|
|
1186
|
-
const targetWithOverrides = {
|
|
1187
|
-
...target,
|
|
1188
|
-
...baseOverrides
|
|
1189
|
-
};
|
|
1190
|
-
return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
|
|
1191
|
-
}).filter((c) => Boolean(c));
|
|
1192
|
-
}
|
|
1193
|
-
return [];
|
|
1194
|
-
}
|
|
1195
1138
|
function sanitizeRelation(relation, sourceCollection, resolveCollection) {
|
|
1196
1139
|
if (!relation.target) {
|
|
1197
1140
|
throw new Error("Relation is missing a `target` collection.");
|
|
@@ -1223,6 +1166,8 @@
|
|
|
1223
1166
|
} else {
|
|
1224
1167
|
targetCollection = evaluated;
|
|
1225
1168
|
}
|
|
1169
|
+
} else if (rawTarget && typeof rawTarget === "object") {
|
|
1170
|
+
targetCollection = rawTarget;
|
|
1226
1171
|
}
|
|
1227
1172
|
if (!targetCollection) {
|
|
1228
1173
|
throw new Error("Relation is missing a valid `target` collection.");
|
|
@@ -1279,8 +1224,8 @@
|
|
|
1279
1224
|
} catch (e) {
|
|
1280
1225
|
}
|
|
1281
1226
|
if (!foundForeignKey) {
|
|
1282
|
-
const
|
|
1283
|
-
newRelation.foreignKeyOnTarget = generateForeignKeyName(
|
|
1227
|
+
const keyPrefix2 = newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName;
|
|
1228
|
+
newRelation.foreignKeyOnTarget = generateForeignKeyName(keyPrefix2);
|
|
1284
1229
|
}
|
|
1285
1230
|
}
|
|
1286
1231
|
} else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
|
|
@@ -1342,11 +1287,14 @@
|
|
|
1342
1287
|
const registeredRelationNames = /* @__PURE__ */ new Set();
|
|
1343
1288
|
if (relCollection.relations) {
|
|
1344
1289
|
relCollection.relations.forEach((relation) => {
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1290
|
+
try {
|
|
1291
|
+
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1292
|
+
const relationKey = normalizedRelation.relationName;
|
|
1293
|
+
if (relationKey) {
|
|
1294
|
+
relations[relationKey] = normalizedRelation;
|
|
1295
|
+
registeredRelationNames.add(relationKey);
|
|
1296
|
+
}
|
|
1297
|
+
} catch (e) {
|
|
1350
1298
|
}
|
|
1351
1299
|
});
|
|
1352
1300
|
}
|
|
@@ -1394,12 +1342,8 @@
|
|
|
1394
1342
|
overrides: relProp.overrides
|
|
1395
1343
|
};
|
|
1396
1344
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
console.warn(`Unrecognized relation format for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1400
|
-
return void 0;
|
|
1401
|
-
}
|
|
1402
|
-
return relation;
|
|
1345
|
+
console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1346
|
+
return void 0;
|
|
1403
1347
|
}
|
|
1404
1348
|
function getTableName(collection) {
|
|
1405
1349
|
if (getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
@@ -1415,6 +1359,43 @@
|
|
|
1415
1359
|
if (snakeKey !== key && resolvedRelations[snakeKey]) return resolvedRelations[snakeKey];
|
|
1416
1360
|
return void 0;
|
|
1417
1361
|
}
|
|
1362
|
+
function getSubcollections(collection) {
|
|
1363
|
+
if (collection.childCollections) {
|
|
1364
|
+
return collection.childCollections() ?? [];
|
|
1365
|
+
}
|
|
1366
|
+
if (getDataSourceCapabilities(collection.driver).supportsSubcollections && collection.subcollections) {
|
|
1367
|
+
return collection.subcollections() ?? [];
|
|
1368
|
+
}
|
|
1369
|
+
if (getDataSourceCapabilities(collection.driver).supportsRelations) {
|
|
1370
|
+
const resolvedRelations = resolveCollectionRelations(collection);
|
|
1371
|
+
const manyRelations = Object.values(resolvedRelations).filter((r) => r.cardinality === "many");
|
|
1372
|
+
return manyRelations.map((r) => {
|
|
1373
|
+
const target = r.target();
|
|
1374
|
+
if (!target) return void 0;
|
|
1375
|
+
const relationKey = r.relationName || target.slug;
|
|
1376
|
+
let customName;
|
|
1377
|
+
if (collection.properties) {
|
|
1378
|
+
const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
|
|
1379
|
+
if (prop && prop[1].name) {
|
|
1380
|
+
customName = prop[1].name;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
const baseOverrides = {
|
|
1384
|
+
slug: relationKey
|
|
1385
|
+
};
|
|
1386
|
+
if (customName) {
|
|
1387
|
+
baseOverrides.name = customName;
|
|
1388
|
+
baseOverrides.singularName = customName;
|
|
1389
|
+
}
|
|
1390
|
+
const targetWithOverrides = {
|
|
1391
|
+
...target,
|
|
1392
|
+
...baseOverrides
|
|
1393
|
+
};
|
|
1394
|
+
return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
|
|
1395
|
+
}).filter((c) => Boolean(c));
|
|
1396
|
+
}
|
|
1397
|
+
return [];
|
|
1398
|
+
}
|
|
1418
1399
|
var logic = { exports: {} };
|
|
1419
1400
|
(function(module2, exports3) {
|
|
1420
1401
|
(function(root, factory) {
|
|
@@ -2322,8 +2303,18 @@
|
|
|
2322
2303
|
const mergedRelationsRaw = [...extractedRelations];
|
|
2323
2304
|
for (const manual of manualRelations) {
|
|
2324
2305
|
const name2 = manual.relationName;
|
|
2325
|
-
if (!name2
|
|
2306
|
+
if (!name2) {
|
|
2326
2307
|
mergedRelationsRaw.push(manual);
|
|
2308
|
+
} else {
|
|
2309
|
+
const existingIndex = mergedRelationsRaw.findIndex((r) => r.relationName === name2);
|
|
2310
|
+
if (existingIndex === -1) {
|
|
2311
|
+
mergedRelationsRaw.push(manual);
|
|
2312
|
+
} else {
|
|
2313
|
+
mergedRelationsRaw[existingIndex] = {
|
|
2314
|
+
...manual,
|
|
2315
|
+
...mergedRelationsRaw[existingIndex]
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2327
2318
|
}
|
|
2328
2319
|
}
|
|
2329
2320
|
let mergedRelations = mergedRelationsRaw;
|
|
@@ -2543,6 +2534,118 @@
|
|
|
2543
2534
|
};
|
|
2544
2535
|
}
|
|
2545
2536
|
}
|
|
2537
|
+
function mapOperator$1(op) {
|
|
2538
|
+
switch (op) {
|
|
2539
|
+
case "==":
|
|
2540
|
+
return "eq";
|
|
2541
|
+
case "!=":
|
|
2542
|
+
return "neq";
|
|
2543
|
+
case ">":
|
|
2544
|
+
return "gt";
|
|
2545
|
+
case ">=":
|
|
2546
|
+
return "gte";
|
|
2547
|
+
case "<":
|
|
2548
|
+
return "lt";
|
|
2549
|
+
case "<=":
|
|
2550
|
+
return "lte";
|
|
2551
|
+
case "array-contains":
|
|
2552
|
+
return "cs";
|
|
2553
|
+
case "array-contains-any":
|
|
2554
|
+
return "csa";
|
|
2555
|
+
case "not-in":
|
|
2556
|
+
return "nin";
|
|
2557
|
+
default:
|
|
2558
|
+
return op;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
class QueryBuilder {
|
|
2562
|
+
constructor(collection) {
|
|
2563
|
+
this.collection = collection;
|
|
2564
|
+
}
|
|
2565
|
+
params = {
|
|
2566
|
+
where: {}
|
|
2567
|
+
};
|
|
2568
|
+
/**
|
|
2569
|
+
* Add a filter condition to your query.
|
|
2570
|
+
* @example
|
|
2571
|
+
* client.collection('users').where('age', '>=', 18).find()
|
|
2572
|
+
*/
|
|
2573
|
+
where(column, operator, value) {
|
|
2574
|
+
if (!this.params.where) {
|
|
2575
|
+
this.params.where = {};
|
|
2576
|
+
}
|
|
2577
|
+
const mappedOp = mapOperator$1(operator);
|
|
2578
|
+
let formattedValue = value;
|
|
2579
|
+
if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
|
|
2580
|
+
formattedValue = `(${value.join(",")})`;
|
|
2581
|
+
} else if (value === null) {
|
|
2582
|
+
formattedValue = "null";
|
|
2583
|
+
}
|
|
2584
|
+
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
2585
|
+
return this;
|
|
2586
|
+
}
|
|
2587
|
+
/**
|
|
2588
|
+
* Order the results by a specific column.
|
|
2589
|
+
* @example
|
|
2590
|
+
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
2591
|
+
*/
|
|
2592
|
+
orderBy(column, ascending = "asc") {
|
|
2593
|
+
this.params.orderBy = `${column}:${ascending}`;
|
|
2594
|
+
return this;
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* Limit the number of results returned.
|
|
2598
|
+
*/
|
|
2599
|
+
limit(count) {
|
|
2600
|
+
this.params.limit = count;
|
|
2601
|
+
return this;
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Skip the first N results.
|
|
2605
|
+
*/
|
|
2606
|
+
offset(count) {
|
|
2607
|
+
this.params.offset = count;
|
|
2608
|
+
return this;
|
|
2609
|
+
}
|
|
2610
|
+
/**
|
|
2611
|
+
* Set a free-text search string if supported by the backend.
|
|
2612
|
+
*/
|
|
2613
|
+
search(searchString) {
|
|
2614
|
+
this.params.searchString = searchString;
|
|
2615
|
+
return this;
|
|
2616
|
+
}
|
|
2617
|
+
/**
|
|
2618
|
+
* Include related entities in the response.
|
|
2619
|
+
* Relations will be populated with full entity data instead of just IDs.
|
|
2620
|
+
*
|
|
2621
|
+
* @param relations - Relation names to include, or "*" for all.
|
|
2622
|
+
* @example
|
|
2623
|
+
* // Include specific relations
|
|
2624
|
+
* client.data.posts.include("tags", "author").find()
|
|
2625
|
+
*
|
|
2626
|
+
* // Include all relations
|
|
2627
|
+
* client.data.posts.include("*").find()
|
|
2628
|
+
*/
|
|
2629
|
+
include(...relations) {
|
|
2630
|
+
this.params.include = relations;
|
|
2631
|
+
return this;
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Execute the find query and return the results.
|
|
2635
|
+
*/
|
|
2636
|
+
async find() {
|
|
2637
|
+
return this.collection.find(this.params);
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Listen to realtime updates matching this query.
|
|
2641
|
+
*/
|
|
2642
|
+
listen(onUpdate, onError) {
|
|
2643
|
+
if (!this.collection.listen) {
|
|
2644
|
+
throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
|
|
2645
|
+
}
|
|
2646
|
+
return this.collection.listen(this.params, onUpdate, onError);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2546
2649
|
class BackendCollectionRegistry extends CollectionRegistry {
|
|
2547
2650
|
/**
|
|
2548
2651
|
* Get the available relation keys for a given collection path.
|
|
@@ -2722,10 +2825,15 @@
|
|
|
2722
2825
|
const issue = error2.code === "42703" ? "column" : "table";
|
|
2723
2826
|
logMessage = `Database schema mismatch (${issue} missing): ${error2.message}. Did you forget to run migrations ('pnpm db:push' or 'pnpm db:migrate')?`;
|
|
2724
2827
|
}
|
|
2725
|
-
console.error(`❌ [API] ${c.req.method} ${c.req.path} → ${statusCode} ${code2}: ${logMessage}`);
|
|
2726
2828
|
const causePg = error2.cause && typeof error2.cause === "object" ? error2.cause : void 0;
|
|
2727
2829
|
const pgErrorCode = causePg?.code || error2.code;
|
|
2728
|
-
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";
|
|
2729
2837
|
if (!suppressStack) {
|
|
2730
2838
|
console.error(error2.stack || error2);
|
|
2731
2839
|
}
|
|
@@ -2770,7 +2878,7 @@
|
|
|
2770
2878
|
};
|
|
2771
2879
|
return map2[code2];
|
|
2772
2880
|
}
|
|
2773
|
-
function mapOperator
|
|
2881
|
+
function mapOperator(op) {
|
|
2774
2882
|
switch (op) {
|
|
2775
2883
|
case "eq":
|
|
2776
2884
|
return "==";
|
|
@@ -2806,7 +2914,7 @@
|
|
|
2806
2914
|
options2.offset = (page - 1) * limit;
|
|
2807
2915
|
}
|
|
2808
2916
|
options2.where = {};
|
|
2809
|
-
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"];
|
|
2810
2918
|
for (const [key, rawValue] of Object.entries(query)) {
|
|
2811
2919
|
if (reservedQueryKeys.includes(key)) continue;
|
|
2812
2920
|
const value = Array.isArray(rawValue) ? rawValue[rawValue.length - 1] : rawValue;
|
|
@@ -2815,7 +2923,7 @@
|
|
|
2815
2923
|
if (parts.length >= 2) {
|
|
2816
2924
|
const op = parts[0];
|
|
2817
2925
|
const val = parts.slice(1).join(".");
|
|
2818
|
-
const rebaseOp = mapOperator
|
|
2926
|
+
const rebaseOp = mapOperator(op);
|
|
2819
2927
|
if (rebaseOp) {
|
|
2820
2928
|
let parsedVal = val;
|
|
2821
2929
|
if (val === "true") parsedVal = true;
|
|
@@ -2878,8 +2986,63 @@
|
|
|
2878
2986
|
const fieldsStr = String(query.fields).trim();
|
|
2879
2987
|
options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
2880
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
|
+
}
|
|
2881
3018
|
return options2;
|
|
2882
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
|
+
}
|
|
2883
3046
|
class RestApiGenerator {
|
|
2884
3047
|
collections;
|
|
2885
3048
|
router;
|
|
@@ -2912,6 +3075,19 @@
|
|
|
2912
3075
|
this.createSubcollectionRoutes();
|
|
2913
3076
|
return this.router;
|
|
2914
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
|
+
}
|
|
2915
3091
|
/**
|
|
2916
3092
|
* Get the typed RestFetchService from a driver if it exposes one (for include support).
|
|
2917
3093
|
*/
|
|
@@ -2925,6 +3101,7 @@
|
|
|
2925
3101
|
const basePath = `/${collection.slug}`;
|
|
2926
3102
|
const resolvedCollection = collection;
|
|
2927
3103
|
this.router.get(`${basePath}/count`, async (c) => {
|
|
3104
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
2928
3105
|
const queryDict = c.req.query();
|
|
2929
3106
|
const queryOptions = parseQueryOptions(queryDict);
|
|
2930
3107
|
const searchString = queryDict.searchString;
|
|
@@ -2935,6 +3112,7 @@
|
|
|
2935
3112
|
});
|
|
2936
3113
|
});
|
|
2937
3114
|
this.router.get(basePath, async (c) => {
|
|
3115
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
2938
3116
|
const queryDict = c.req.query();
|
|
2939
3117
|
const queryOptions = parseQueryOptions(queryDict);
|
|
2940
3118
|
const searchString = queryDict.searchString;
|
|
@@ -2949,7 +3127,8 @@
|
|
|
2949
3127
|
offset: queryOptions.offset,
|
|
2950
3128
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
2951
3129
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
2952
|
-
searchString
|
|
3130
|
+
searchString,
|
|
3131
|
+
vectorSearch: queryOptions.vectorSearch
|
|
2953
3132
|
}, queryOptions.include);
|
|
2954
3133
|
entities2 = await this.applyAfterReadBatch(collection.slug, entities2, hookCtx);
|
|
2955
3134
|
const total2 = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
|
|
@@ -2977,6 +3156,7 @@
|
|
|
2977
3156
|
});
|
|
2978
3157
|
});
|
|
2979
3158
|
this.router.get(`${basePath}/:id`, async (c) => {
|
|
3159
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
2980
3160
|
const id = c.req.param("id");
|
|
2981
3161
|
const queryDict = c.req.query();
|
|
2982
3162
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3007,6 +3187,7 @@
|
|
|
3007
3187
|
});
|
|
3008
3188
|
this.router.post(basePath, async (c) => {
|
|
3009
3189
|
try {
|
|
3190
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3010
3191
|
const driver = c.get("driver") || this.driver;
|
|
3011
3192
|
const path2 = collection.slug;
|
|
3012
3193
|
const hookCtx = this.buildHookContext(c, "POST");
|
|
@@ -3035,6 +3216,7 @@
|
|
|
3035
3216
|
});
|
|
3036
3217
|
this.router.put(`${basePath}/:id`, async (c) => {
|
|
3037
3218
|
try {
|
|
3219
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3038
3220
|
const id = c.req.param("id");
|
|
3039
3221
|
const driver = c.get("driver") || this.driver;
|
|
3040
3222
|
const hookCtx = this.buildHookContext(c, "PUT");
|
|
@@ -3071,6 +3253,7 @@
|
|
|
3071
3253
|
}
|
|
3072
3254
|
});
|
|
3073
3255
|
this.router.delete(`${basePath}/:id`, async (c) => {
|
|
3256
|
+
this.enforceApiKeyPermission(c, collection.slug);
|
|
3074
3257
|
const id = c.req.param("id");
|
|
3075
3258
|
const driver = c.get("driver") || this.driver;
|
|
3076
3259
|
const hookCtx = this.buildHookContext(c, "DELETE");
|
|
@@ -3142,6 +3325,7 @@
|
|
|
3142
3325
|
const parsed = parseSubPath(rawPath);
|
|
3143
3326
|
if (!parsed) return next();
|
|
3144
3327
|
const driver = c.get("driver") || this.driver;
|
|
3328
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3145
3329
|
if (parsed.entityId === "count") {
|
|
3146
3330
|
const queryDict = c.req.query();
|
|
3147
3331
|
const queryOptions = parseQueryOptions(queryDict);
|
|
@@ -3189,6 +3373,7 @@
|
|
|
3189
3373
|
const parsed = parseSubPath(rawPath);
|
|
3190
3374
|
if (!parsed || parsed.entityId) return next();
|
|
3191
3375
|
const driver = c.get("driver") || this.driver;
|
|
3376
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3192
3377
|
const body = await c.req.json().catch(() => ({}));
|
|
3193
3378
|
const entity = await driver.saveEntity({
|
|
3194
3379
|
path: parsed.collectionPath,
|
|
@@ -3204,6 +3389,7 @@
|
|
|
3204
3389
|
const parsed = parseSubPath(rawPath);
|
|
3205
3390
|
if (!parsed || !parsed.entityId) return next();
|
|
3206
3391
|
const driver = c.get("driver") || this.driver;
|
|
3392
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3207
3393
|
const body = await c.req.json().catch(() => ({}));
|
|
3208
3394
|
const entity = await driver.saveEntity({
|
|
3209
3395
|
path: parsed.collectionPath,
|
|
@@ -3220,6 +3406,7 @@
|
|
|
3220
3406
|
const parsed = parseSubPath(rawPath);
|
|
3221
3407
|
if (!parsed || !parsed.entityId) return next();
|
|
3222
3408
|
const driver = c.get("driver") || this.driver;
|
|
3409
|
+
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3223
3410
|
const existingEntity = await driver.fetchEntity({
|
|
3224
3411
|
path: parsed.collectionPath,
|
|
3225
3412
|
entityId: parsed.entityId
|
|
@@ -3285,7 +3472,8 @@
|
|
|
3285
3472
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
3286
3473
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3287
3474
|
startAfter: queryOptions.offset ? String(queryOptions.offset) : void 0,
|
|
3288
|
-
searchString
|
|
3475
|
+
searchString,
|
|
3476
|
+
vectorSearch: queryOptions.vectorSearch
|
|
3289
3477
|
});
|
|
3290
3478
|
return entities.map((entity) => this.flattenEntity(entity));
|
|
3291
3479
|
}
|
|
@@ -4811,7 +4999,7 @@
|
|
|
4811
4999
|
const SemVer$6 = semver$4;
|
|
4812
5000
|
const parse$6 = parse_1;
|
|
4813
5001
|
const { safeRe: re, t } = reExports;
|
|
4814
|
-
const coerce$
|
|
5002
|
+
const coerce$2 = (version2, options2) => {
|
|
4815
5003
|
if (version2 instanceof SemVer$6) {
|
|
4816
5004
|
return version2;
|
|
4817
5005
|
}
|
|
@@ -4846,7 +5034,7 @@
|
|
|
4846
5034
|
const build = options2.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
4847
5035
|
return parse$6(`${major2}.${minor2}.${patch2}${prerelease2}${build}`, options2);
|
|
4848
5036
|
};
|
|
4849
|
-
var coerce_1 = coerce$
|
|
5037
|
+
var coerce_1 = coerce$2;
|
|
4850
5038
|
const parse$5 = parse_1;
|
|
4851
5039
|
const constants$1 = constants$2;
|
|
4852
5040
|
const SemVer$5 = semver$4;
|
|
@@ -5132,19 +5320,19 @@
|
|
|
5132
5320
|
const replaceCaret = (comp, options2) => {
|
|
5133
5321
|
debug2("caret", comp, options2);
|
|
5134
5322
|
const r = options2.loose ? re2[t2.CARETLOOSE] : re2[t2.CARET];
|
|
5135
|
-
const
|
|
5323
|
+
const z2 = options2.includePrerelease ? "-0" : "";
|
|
5136
5324
|
return comp.replace(r, (_, M, m2, p, pr) => {
|
|
5137
5325
|
debug2("caret", comp, _, M, m2, p, pr);
|
|
5138
5326
|
let ret;
|
|
5139
5327
|
if (isX(M)) {
|
|
5140
5328
|
ret = "";
|
|
5141
5329
|
} else if (isX(m2)) {
|
|
5142
|
-
ret = `>=${M}.0.0${
|
|
5330
|
+
ret = `>=${M}.0.0${z2} <${+M + 1}.0.0-0`;
|
|
5143
5331
|
} else if (isX(p)) {
|
|
5144
5332
|
if (M === "0") {
|
|
5145
|
-
ret = `>=${M}.${m2}.0${
|
|
5333
|
+
ret = `>=${M}.${m2}.0${z2} <${M}.${+m2 + 1}.0-0`;
|
|
5146
5334
|
} else {
|
|
5147
|
-
ret = `>=${M}.${m2}.0${
|
|
5335
|
+
ret = `>=${M}.${m2}.0${z2} <${+M + 1}.0.0-0`;
|
|
5148
5336
|
}
|
|
5149
5337
|
} else if (pr) {
|
|
5150
5338
|
debug2("replaceCaret pr", pr);
|
|
@@ -5161,9 +5349,9 @@
|
|
|
5161
5349
|
debug2("no pr");
|
|
5162
5350
|
if (M === "0") {
|
|
5163
5351
|
if (m2 === "0") {
|
|
5164
|
-
ret = `>=${M}.${m2}.${p}${
|
|
5352
|
+
ret = `>=${M}.${m2}.${p}${z2} <${M}.${m2}.${+p + 1}-0`;
|
|
5165
5353
|
} else {
|
|
5166
|
-
ret = `>=${M}.${m2}.${p}${
|
|
5354
|
+
ret = `>=${M}.${m2}.${p}${z2} <${M}.${+m2 + 1}.0-0`;
|
|
5167
5355
|
}
|
|
5168
5356
|
} else {
|
|
5169
5357
|
ret = `>=${M}.${m2}.${p} <${+M + 1}.0.0-0`;
|
|
@@ -5820,7 +6008,7 @@
|
|
|
5820
6008
|
const gte = gte_1;
|
|
5821
6009
|
const lte = lte_1;
|
|
5822
6010
|
const cmp = cmp_1;
|
|
5823
|
-
const coerce = coerce_1;
|
|
6011
|
+
const coerce$1 = coerce_1;
|
|
5824
6012
|
const truncate = truncate_1;
|
|
5825
6013
|
const Comparator = requireComparator();
|
|
5826
6014
|
const Range = requireRange();
|
|
@@ -5859,7 +6047,7 @@
|
|
|
5859
6047
|
gte,
|
|
5860
6048
|
lte,
|
|
5861
6049
|
cmp,
|
|
5862
|
-
coerce,
|
|
6050
|
+
coerce: coerce$1,
|
|
5863
6051
|
truncate,
|
|
5864
6052
|
Comparator,
|
|
5865
6053
|
Range,
|
|
@@ -6740,7 +6928,7 @@
|
|
|
6740
6928
|
NotBeforeError: NotBeforeError_1,
|
|
6741
6929
|
TokenExpiredError: TokenExpiredError_1
|
|
6742
6930
|
};
|
|
6743
|
-
const jwt = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
|
|
6931
|
+
const jwt$1 = /* @__PURE__ */ getDefaultExportFromCjs(jsonwebtoken);
|
|
6744
6932
|
let jwtConfig = {
|
|
6745
6933
|
secret: "",
|
|
6746
6934
|
accessExpiresIn: "1h",
|
|
@@ -6759,15 +6947,17 @@
|
|
|
6759
6947
|
...config
|
|
6760
6948
|
};
|
|
6761
6949
|
}
|
|
6762
|
-
function generateAccessToken(userId, roles) {
|
|
6950
|
+
function generateAccessToken(userId, roles, aal = "aal1", customClaims) {
|
|
6763
6951
|
if (!jwtConfig.secret) {
|
|
6764
6952
|
throw new Error("JWT secret not configured. Call configureJwt() first.");
|
|
6765
6953
|
}
|
|
6766
6954
|
const payload = {
|
|
6767
6955
|
userId,
|
|
6768
|
-
roles
|
|
6956
|
+
roles,
|
|
6957
|
+
aal,
|
|
6958
|
+
...customClaims
|
|
6769
6959
|
};
|
|
6770
|
-
return jwt.sign(payload, jwtConfig.secret, {
|
|
6960
|
+
return jwt$1.sign(payload, jwtConfig.secret, {
|
|
6771
6961
|
expiresIn: jwtConfig.accessExpiresIn,
|
|
6772
6962
|
algorithm: "HS256"
|
|
6773
6963
|
});
|
|
@@ -6801,7 +6991,7 @@
|
|
|
6801
6991
|
throw new Error("JWT secret not configured. Call configureJwt() first.");
|
|
6802
6992
|
}
|
|
6803
6993
|
try {
|
|
6804
|
-
const decoded = jwt.verify(token, jwtConfig.secret, {
|
|
6994
|
+
const decoded = jwt$1.verify(token, jwtConfig.secret, {
|
|
6805
6995
|
algorithms: ["HS256"]
|
|
6806
6996
|
});
|
|
6807
6997
|
const id = decoded.userId || decoded.uid || decoded.sub;
|
|
@@ -6809,9 +6999,11 @@
|
|
|
6809
6999
|
console.error("[JWT] Verification failed: missing id in payload", decoded);
|
|
6810
7000
|
return null;
|
|
6811
7001
|
}
|
|
7002
|
+
const aal = decoded.aal === "aal1" || decoded.aal === "aal2" ? decoded.aal : "aal1";
|
|
6812
7003
|
return {
|
|
6813
7004
|
userId: id,
|
|
6814
|
-
roles: decoded.roles || []
|
|
7005
|
+
roles: decoded.roles || [],
|
|
7006
|
+
aal
|
|
6815
7007
|
};
|
|
6816
7008
|
} catch (error2) {
|
|
6817
7009
|
console.error("[JWT] Verification failed:", error2, "Token start:", token.substring(0, 15));
|
|
@@ -6851,6 +7043,17 @@
|
|
|
6851
7043
|
}
|
|
6852
7044
|
return new Date(Date.now() + ms2);
|
|
6853
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" }));
|
|
6854
7057
|
function isRLSScopedDriver(driver) {
|
|
6855
7058
|
return "withAuth" in driver && typeof driver.withAuth === "function";
|
|
6856
7059
|
}
|
|
@@ -6873,6 +7076,81 @@
|
|
|
6873
7076
|
return false;
|
|
6874
7077
|
}
|
|
6875
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
|
+
}
|
|
6876
7154
|
const requireAuth = async (c, next) => {
|
|
6877
7155
|
const authHeader = c.req.header("authorization");
|
|
6878
7156
|
const queryToken = c.req.query("token");
|
|
@@ -6979,7 +7257,8 @@
|
|
|
6979
7257
|
driver,
|
|
6980
7258
|
requireAuth: enforceAuth = true,
|
|
6981
7259
|
validator,
|
|
6982
|
-
serviceKey
|
|
7260
|
+
serviceKey,
|
|
7261
|
+
apiKeyStore
|
|
6983
7262
|
} = options2;
|
|
6984
7263
|
return async (c, next) => {
|
|
6985
7264
|
if (validator) {
|
|
@@ -7054,6 +7333,14 @@
|
|
|
7054
7333
|
}
|
|
7055
7334
|
}, 500);
|
|
7056
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
|
+
}
|
|
7057
7344
|
} else {
|
|
7058
7345
|
const payload = extractUserFromToken(token);
|
|
7059
7346
|
if (payload) {
|
|
@@ -7114,9 +7401,22 @@
|
|
|
7114
7401
|
const {
|
|
7115
7402
|
adapter,
|
|
7116
7403
|
driver,
|
|
7117
|
-
requireAuth: enforceAuth = true
|
|
7404
|
+
requireAuth: enforceAuth = true,
|
|
7405
|
+
apiKeyStore
|
|
7118
7406
|
} = options2;
|
|
7119
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
|
+
}
|
|
7120
7420
|
let authenticatedUser = null;
|
|
7121
7421
|
try {
|
|
7122
7422
|
authenticatedUser = await adapter.verifyRequest(c.req.raw);
|
|
@@ -7212,11 +7512,11 @@
|
|
|
7212
7512
|
const derivedKey = await scryptAsync(password, salt, KEY_LENGTH);
|
|
7213
7513
|
return require$$0$5.timingSafeEqual(derivedKey, storedKey);
|
|
7214
7514
|
}
|
|
7215
|
-
function
|
|
7515
|
+
function resolveAuthHooks(hooks) {
|
|
7216
7516
|
return {
|
|
7217
|
-
hashPassword:
|
|
7218
|
-
verifyPassword:
|
|
7219
|
-
validatePasswordStrength:
|
|
7517
|
+
hashPassword: hooks?.hashPassword ?? hashPassword,
|
|
7518
|
+
verifyPassword: hooks?.verifyPassword ?? verifyPassword,
|
|
7519
|
+
validatePasswordStrength: hooks?.validatePasswordStrength ?? validatePasswordStrength
|
|
7220
7520
|
};
|
|
7221
7521
|
}
|
|
7222
7522
|
function getGreeting(user) {
|
|
@@ -7618,6 +7918,63 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7618
7918
|
limit: 50,
|
|
7619
7919
|
message: "Too many requests to this sensitive endpoint, please try again later."
|
|
7620
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
|
+
}
|
|
7621
7978
|
var util$3;
|
|
7622
7979
|
(function(util2) {
|
|
7623
7980
|
util2.assertEqual = (_) => {
|
|
@@ -7768,6 +8125,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7768
8125
|
"not_multiple_of",
|
|
7769
8126
|
"not_finite"
|
|
7770
8127
|
]);
|
|
8128
|
+
const quotelessJson = (obj) => {
|
|
8129
|
+
const json = JSON.stringify(obj, null, 2);
|
|
8130
|
+
return json.replace(/"([^"]+)":/g, "$1:");
|
|
8131
|
+
};
|
|
7771
8132
|
class ZodError extends Error {
|
|
7772
8133
|
get errors() {
|
|
7773
8134
|
return this.issues;
|
|
@@ -7963,6 +8324,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7963
8324
|
return { message };
|
|
7964
8325
|
};
|
|
7965
8326
|
let overrideErrorMap = errorMap;
|
|
8327
|
+
function setErrorMap(map2) {
|
|
8328
|
+
overrideErrorMap = map2;
|
|
8329
|
+
}
|
|
7966
8330
|
function getErrorMap() {
|
|
7967
8331
|
return overrideErrorMap;
|
|
7968
8332
|
}
|
|
@@ -7991,6 +8355,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
7991
8355
|
message: errorMessage
|
|
7992
8356
|
};
|
|
7993
8357
|
};
|
|
8358
|
+
const EMPTY_PATH = [];
|
|
7994
8359
|
function addIssueToContext(ctx, issueData) {
|
|
7995
8360
|
const overrideMap = getErrorMap();
|
|
7996
8361
|
const issue = makeIssue({
|
|
@@ -10281,6 +10646,113 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10281
10646
|
...processCreateParams(params)
|
|
10282
10647
|
});
|
|
10283
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
|
+
}
|
|
10284
10756
|
function mergeValues(a2, b) {
|
|
10285
10757
|
const aType = getParsedType(a2);
|
|
10286
10758
|
const bType = getParsedType(b);
|
|
@@ -10439,6 +10911,59 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10439
10911
|
...processCreateParams(params)
|
|
10440
10912
|
});
|
|
10441
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
|
+
}
|
|
10442
10967
|
class ZodMap extends ZodType {
|
|
10443
10968
|
get keySchema() {
|
|
10444
10969
|
return this._def.keyType;
|
|
@@ -10590,6 +11115,111 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
10590
11115
|
...processCreateParams(params)
|
|
10591
11116
|
});
|
|
10592
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
|
+
}
|
|
10593
11223
|
class ZodLazy extends ZodType {
|
|
10594
11224
|
get schema() {
|
|
10595
11225
|
return this._def.getter();
|
|
@@ -11047,6 +11677,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11047
11677
|
...processCreateParams(params)
|
|
11048
11678
|
});
|
|
11049
11679
|
};
|
|
11680
|
+
const BRAND = Symbol("zod_brand");
|
|
11050
11681
|
class ZodBranded extends ZodType {
|
|
11051
11682
|
_parse(input) {
|
|
11052
11683
|
const { ctx } = this._processInputParams(input);
|
|
@@ -11138,6 +11769,36 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11138
11769
|
...processCreateParams(params)
|
|
11139
11770
|
});
|
|
11140
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
|
+
};
|
|
11141
11802
|
var ZodFirstPartyTypeKind;
|
|
11142
11803
|
(function(ZodFirstPartyTypeKind2) {
|
|
11143
11804
|
ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
|
|
@@ -11177,17 +11838,251 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11177
11838
|
ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
|
|
11178
11839
|
ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
|
|
11179
11840
|
})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
|
|
11841
|
+
const instanceOfType = (cls, params = {
|
|
11842
|
+
message: `Input not instance of ${cls.name}`
|
|
11843
|
+
}) => custom((data) => data instanceof cls, params);
|
|
11180
11844
|
const stringType = ZodString.create;
|
|
11181
|
-
|
|
11182
|
-
|
|
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;
|
|
11183
11858
|
const objectType = ZodObject.create;
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
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;
|
|
11187
11870
|
const enumType = ZodEnum.create;
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
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
|
+
}
|
|
11191
12086
|
function buildAuthResponse(user, roleIds, accessToken, refreshToken) {
|
|
11192
12087
|
return {
|
|
11193
12088
|
user: {
|
|
@@ -11225,9 +12120,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11225
12120
|
emailService,
|
|
11226
12121
|
emailConfig,
|
|
11227
12122
|
allowRegistration = false,
|
|
11228
|
-
|
|
12123
|
+
authHooks
|
|
11229
12124
|
} = config;
|
|
11230
|
-
const ops =
|
|
12125
|
+
const ops = resolveAuthHooks(authHooks);
|
|
11231
12126
|
const registerSchema = objectType({
|
|
11232
12127
|
email: stringType().email("Invalid email address").max(255),
|
|
11233
12128
|
password: stringType().min(1, "Password is required").max(128),
|
|
@@ -11291,7 +12186,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11291
12186
|
async function createSessionAndTokens(userId, userAgent, ipAddress) {
|
|
11292
12187
|
const roles = await authRepo.getUserRoles(userId);
|
|
11293
12188
|
const roleIds = roles.map((r) => r.id);
|
|
11294
|
-
|
|
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);
|
|
11295
12202
|
const refreshToken = generateRefreshToken();
|
|
11296
12203
|
await authRepo.createRefreshToken(userId, hashRefreshToken(refreshToken), getRefreshTokenExpiry(), userAgent, ipAddress);
|
|
11297
12204
|
return {
|
|
@@ -11326,8 +12233,8 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11326
12233
|
passwordHash,
|
|
11327
12234
|
displayName: displayName || void 0
|
|
11328
12235
|
};
|
|
11329
|
-
if (
|
|
11330
|
-
createData = await
|
|
12236
|
+
if (authHooks?.beforeUserCreate) {
|
|
12237
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
11331
12238
|
}
|
|
11332
12239
|
const user = await authRepo.createUser(createData);
|
|
11333
12240
|
const existingUsers = await authRepo.listUsers();
|
|
@@ -11346,14 +12253,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11346
12253
|
email: user.email,
|
|
11347
12254
|
displayName: user.displayName
|
|
11348
12255
|
});
|
|
11349
|
-
if (
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
})
|
|
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
|
+
}
|
|
11353
12262
|
}
|
|
11354
|
-
if (
|
|
11355
|
-
|
|
11356
|
-
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);
|
|
11357
12266
|
});
|
|
11358
12267
|
}
|
|
11359
12268
|
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken), 201);
|
|
@@ -11363,9 +12272,12 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11363
12272
|
email,
|
|
11364
12273
|
password
|
|
11365
12274
|
} = parseBody2(loginSchema, await c.req.json());
|
|
12275
|
+
if (authHooks?.beforeLogin) {
|
|
12276
|
+
await authHooks.beforeLogin(email, "login");
|
|
12277
|
+
}
|
|
11366
12278
|
let user;
|
|
11367
|
-
if (
|
|
11368
|
-
user = await
|
|
12279
|
+
if (authHooks?.verifyCredentials) {
|
|
12280
|
+
user = await authHooks.verifyCredentials(email, password, authRepo);
|
|
11369
12281
|
if (!user) {
|
|
11370
12282
|
throw ApiError.unauthorized("Invalid email or password", "INVALID_CREDENTIALS");
|
|
11371
12283
|
}
|
|
@@ -11387,9 +12299,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11387
12299
|
accessToken,
|
|
11388
12300
|
refreshToken
|
|
11389
12301
|
} = await createSessionAndTokens(user.id, c.req.header("user-agent") || "unknown", c.req.header("x-forwarded-for") || "unknown");
|
|
11390
|
-
if (
|
|
11391
|
-
|
|
11392
|
-
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);
|
|
11393
12305
|
});
|
|
11394
12306
|
}
|
|
11395
12307
|
return c.json(buildAuthResponse(user, roleIds, accessToken, refreshToken));
|
|
@@ -11428,6 +12340,13 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11428
12340
|
await authRepo.linkUserIdentity(user.id, provider.id, externalUser.providerId, {
|
|
11429
12341
|
email: externalUser.email
|
|
11430
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
|
+
}
|
|
11431
12350
|
const allUsers = await authRepo.listUsers();
|
|
11432
12351
|
const isFirstUser = allUsers.length === 1 && allUsers[0].id === user.id;
|
|
11433
12352
|
if (isFirstUser) {
|
|
@@ -11513,6 +12432,11 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11513
12432
|
await authRepo.updatePassword(storedToken.userId, passwordHash);
|
|
11514
12433
|
await authRepo.markPasswordResetTokenUsed(tokenHash);
|
|
11515
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
|
+
}
|
|
11516
12440
|
return c.json({
|
|
11517
12441
|
success: true,
|
|
11518
12442
|
message: "Password has been reset successfully"
|
|
@@ -11616,7 +12540,19 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11616
12540
|
}
|
|
11617
12541
|
const roles = await authRepo.getUserRoles(storedToken.userId);
|
|
11618
12542
|
const roleIds = roles.map((r) => r.id);
|
|
11619
|
-
|
|
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);
|
|
11620
12556
|
const newRefreshToken = generateRefreshToken();
|
|
11621
12557
|
const userAgent = c.req.header("user-agent") || "unknown";
|
|
11622
12558
|
const ipAddress = c.req.header("x-forwarded-for") || "unknown";
|
|
@@ -11638,6 +12574,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11638
12574
|
const tokenHash = hashRefreshToken(refreshToken);
|
|
11639
12575
|
await authRepo.deleteRefreshToken(tokenHash);
|
|
11640
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
|
+
}
|
|
11641
12589
|
return c.json({
|
|
11642
12590
|
success: true
|
|
11643
12591
|
});
|
|
@@ -11757,6 +12705,270 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11757
12705
|
enabledProviders
|
|
11758
12706
|
});
|
|
11759
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
|
+
});
|
|
11760
12972
|
return router;
|
|
11761
12973
|
}
|
|
11762
12974
|
function generateSecurePassword() {
|
|
@@ -11788,9 +13000,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11788
13000
|
emailService,
|
|
11789
13001
|
emailConfig,
|
|
11790
13002
|
hooks,
|
|
11791
|
-
|
|
13003
|
+
authHooks
|
|
11792
13004
|
} = config;
|
|
11793
|
-
const ops =
|
|
13005
|
+
const ops = resolveAuthHooks(authHooks);
|
|
11794
13006
|
function buildHookContext(c, method) {
|
|
11795
13007
|
const user = c.get("user");
|
|
11796
13008
|
return {
|
|
@@ -11858,6 +13070,10 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
11858
13070
|
if (!userId) {
|
|
11859
13071
|
throw ApiError.unauthorized("User ID not found in auth context");
|
|
11860
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
|
+
}
|
|
11861
13077
|
await authRepo.setUserRoles(userId, ["admin"]);
|
|
11862
13078
|
if (config.setBootstrapCompleted) {
|
|
11863
13079
|
await config.setBootstrapCompleted();
|
|
@@ -12109,6 +13325,18 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12109
13325
|
await authRepo.updateUser(userId, updates);
|
|
12110
13326
|
}
|
|
12111
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
|
+
}
|
|
12112
13340
|
await authRepo.setUserRoles(userId, roles);
|
|
12113
13341
|
}
|
|
12114
13342
|
const result = await authRepo.getUserWithRoles(userId);
|
|
@@ -12133,6 +13361,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12133
13361
|
if (!existing) {
|
|
12134
13362
|
throw ApiError.notFound("User not found");
|
|
12135
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
|
+
}
|
|
12136
13374
|
const hookCtx = buildHookContext(c, "DELETE");
|
|
12137
13375
|
if (hooks?.users?.beforeDelete) {
|
|
12138
13376
|
await hooks.users.beforeDelete(userId, hookCtx);
|
|
@@ -12154,8 +13392,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12154
13392
|
id: r.id,
|
|
12155
13393
|
name: r.name,
|
|
12156
13394
|
isAdmin: r.isAdmin,
|
|
12157
|
-
defaultPermissions: r.defaultPermissions
|
|
12158
|
-
config: r.config
|
|
13395
|
+
defaultPermissions: r.defaultPermissions
|
|
12159
13396
|
}));
|
|
12160
13397
|
adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
|
|
12161
13398
|
return c.json({
|
|
@@ -12178,8 +13415,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12178
13415
|
id,
|
|
12179
13416
|
name: name2,
|
|
12180
13417
|
isAdmin,
|
|
12181
|
-
defaultPermissions
|
|
12182
|
-
config: config2
|
|
13418
|
+
defaultPermissions
|
|
12183
13419
|
} = body;
|
|
12184
13420
|
if (!id || !name2) {
|
|
12185
13421
|
throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
|
|
@@ -12192,8 +13428,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12192
13428
|
id,
|
|
12193
13429
|
name: name2,
|
|
12194
13430
|
isAdmin: isAdmin ?? false,
|
|
12195
|
-
defaultPermissions: defaultPermissions ?? null
|
|
12196
|
-
config: config2 ?? null
|
|
13431
|
+
defaultPermissions: defaultPermissions ?? null
|
|
12197
13432
|
});
|
|
12198
13433
|
return c.json({
|
|
12199
13434
|
role
|
|
@@ -12205,8 +13440,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12205
13440
|
const {
|
|
12206
13441
|
name: name2,
|
|
12207
13442
|
isAdmin,
|
|
12208
|
-
defaultPermissions
|
|
12209
|
-
config: config2
|
|
13443
|
+
defaultPermissions
|
|
12210
13444
|
} = body;
|
|
12211
13445
|
const existing = await authRepo.getRoleById(roleId);
|
|
12212
13446
|
if (!existing) {
|
|
@@ -12215,8 +13449,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12215
13449
|
const role = await authRepo.updateRole(roleId, {
|
|
12216
13450
|
name: name2,
|
|
12217
13451
|
isAdmin,
|
|
12218
|
-
defaultPermissions
|
|
12219
|
-
config: config2
|
|
13452
|
+
defaultPermissions
|
|
12220
13453
|
});
|
|
12221
13454
|
return c.json({
|
|
12222
13455
|
role
|
|
@@ -12248,9 +13481,9 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12248
13481
|
oauthProviders = [],
|
|
12249
13482
|
serviceKey,
|
|
12250
13483
|
hooks,
|
|
12251
|
-
|
|
13484
|
+
authHooks
|
|
12252
13485
|
} = config;
|
|
12253
|
-
const resolvedOps =
|
|
13486
|
+
const resolvedOps = resolveAuthHooks(authHooks);
|
|
12254
13487
|
const adapter = {
|
|
12255
13488
|
id: "rebase-builtin",
|
|
12256
13489
|
serviceKey,
|
|
@@ -12324,7 +13557,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12324
13557
|
rawToken: token
|
|
12325
13558
|
};
|
|
12326
13559
|
},
|
|
12327
|
-
userManagement: createUserManagementFromRepo(authRepository, resolvedOps,
|
|
13560
|
+
userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
|
|
12328
13561
|
roleManagement: createRoleManagementFromRepo(authRepository),
|
|
12329
13562
|
createAuthRoutes() {
|
|
12330
13563
|
return createAuthRoutes({
|
|
@@ -12334,7 +13567,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12334
13567
|
allowRegistration,
|
|
12335
13568
|
defaultRole,
|
|
12336
13569
|
oauthProviders,
|
|
12337
|
-
|
|
13570
|
+
authHooks
|
|
12338
13571
|
});
|
|
12339
13572
|
},
|
|
12340
13573
|
createAdminRoutes() {
|
|
@@ -12344,7 +13577,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12344
13577
|
emailConfig,
|
|
12345
13578
|
serviceKey,
|
|
12346
13579
|
hooks,
|
|
12347
|
-
|
|
13580
|
+
authHooks
|
|
12348
13581
|
});
|
|
12349
13582
|
},
|
|
12350
13583
|
async getCapabilities() {
|
|
@@ -12373,7 +13606,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12373
13606
|
};
|
|
12374
13607
|
return adapter;
|
|
12375
13608
|
}
|
|
12376
|
-
function createUserManagementFromRepo(repo, resolvedOps,
|
|
13609
|
+
function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
|
|
12377
13610
|
return {
|
|
12378
13611
|
async listUsers(options2) {
|
|
12379
13612
|
const result = await repo.listUsersPaginated({
|
|
@@ -12404,14 +13637,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12404
13637
|
photoUrl: data.photoUrl,
|
|
12405
13638
|
metadata: data.metadata
|
|
12406
13639
|
};
|
|
12407
|
-
if (
|
|
12408
|
-
createData = await
|
|
13640
|
+
if (authHooks?.beforeUserCreate) {
|
|
13641
|
+
createData = await authHooks.beforeUserCreate(createData);
|
|
12409
13642
|
}
|
|
12410
13643
|
const user = await repo.createUser(createData);
|
|
12411
|
-
if (
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
})
|
|
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
|
+
}
|
|
12415
13650
|
}
|
|
12416
13651
|
return toAuthUserData(user);
|
|
12417
13652
|
},
|
|
@@ -12428,7 +13663,15 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12428
13663
|
return user ? toAuthUserData(user) : null;
|
|
12429
13664
|
},
|
|
12430
13665
|
async deleteUser(id) {
|
|
13666
|
+
if (authHooks?.beforeUserDelete) {
|
|
13667
|
+
await authHooks.beforeUserDelete(id);
|
|
13668
|
+
}
|
|
12431
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
|
+
}
|
|
12432
13675
|
},
|
|
12433
13676
|
async getUserRoles(userId) {
|
|
12434
13677
|
const roles = await repo.getUserRoles(userId);
|
|
@@ -12455,8 +13698,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12455
13698
|
name: data.name,
|
|
12456
13699
|
isAdmin: data.isAdmin,
|
|
12457
13700
|
defaultPermissions: data.defaultPermissions,
|
|
12458
|
-
collectionPermissions: data.collectionPermissions
|
|
12459
|
-
config: data.config
|
|
13701
|
+
collectionPermissions: data.collectionPermissions
|
|
12460
13702
|
});
|
|
12461
13703
|
return toAuthRoleData(role);
|
|
12462
13704
|
},
|
|
@@ -12487,8 +13729,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12487
13729
|
name: role.name,
|
|
12488
13730
|
isAdmin: role.isAdmin,
|
|
12489
13731
|
defaultPermissions: role.defaultPermissions,
|
|
12490
|
-
collectionPermissions: role.collectionPermissions
|
|
12491
|
-
config: role.config
|
|
13732
|
+
collectionPermissions: role.collectionPermissions
|
|
12492
13733
|
};
|
|
12493
13734
|
}
|
|
12494
13735
|
function configureLogLevel(logLevel) {
|
|
@@ -12509,20 +13750,20 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12509
13750
|
if (currentLevel < 0) console.error = () => {
|
|
12510
13751
|
};
|
|
12511
13752
|
}
|
|
13753
|
+
let originalConsole;
|
|
12512
13754
|
function resetConsole() {
|
|
12513
|
-
if (!
|
|
12514
|
-
|
|
13755
|
+
if (!originalConsole) {
|
|
13756
|
+
originalConsole = {
|
|
12515
13757
|
log: console.log,
|
|
12516
13758
|
warn: console.warn,
|
|
12517
13759
|
error: console.error,
|
|
12518
13760
|
debug: console.debug
|
|
12519
13761
|
};
|
|
12520
13762
|
}
|
|
12521
|
-
|
|
12522
|
-
console.
|
|
12523
|
-
console.
|
|
12524
|
-
console.
|
|
12525
|
-
console.debug = original.debug;
|
|
13763
|
+
console.log = originalConsole.log;
|
|
13764
|
+
console.warn = originalConsole.warn;
|
|
13765
|
+
console.error = originalConsole.error;
|
|
13766
|
+
console.debug = originalConsole.debug;
|
|
12526
13767
|
}
|
|
12527
13768
|
const GCP_SEVERITY = {
|
|
12528
13769
|
debug: "DEBUG",
|
|
@@ -12764,12 +14005,6 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
12764
14005
|
exports3.Response = globalObject.Response;
|
|
12765
14006
|
})(browser$1, browser$1.exports);
|
|
12766
14007
|
var browserExports = browser$1.exports;
|
|
12767
|
-
const __viteBrowserExternal = {};
|
|
12768
|
-
const __viteBrowserExternal$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
12769
|
-
__proto__: null,
|
|
12770
|
-
default: __viteBrowserExternal
|
|
12771
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
12772
|
-
const require$$11 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal$1);
|
|
12773
14008
|
const isStream = (stream2) => stream2 !== null && typeof stream2 === "object" && typeof stream2.pipe === "function";
|
|
12774
14009
|
isStream.writable = (stream2) => isStream(stream2) && stream2.writable !== false && typeof stream2._write === "function" && typeof stream2._writableState === "object";
|
|
12775
14010
|
isStream.readable = (stream2) => isStream(stream2) && stream2.readable !== false && typeof stream2._read === "function" && typeof stream2._readableState === "object";
|
|
@@ -13554,16 +14789,16 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
13554
14789
|
value: true
|
|
13555
14790
|
});
|
|
13556
14791
|
sha1$1.default = void 0;
|
|
13557
|
-
function f(s2, x, y2,
|
|
14792
|
+
function f(s2, x, y2, z2) {
|
|
13558
14793
|
switch (s2) {
|
|
13559
14794
|
case 0:
|
|
13560
|
-
return x & y2 ^ ~x &
|
|
14795
|
+
return x & y2 ^ ~x & z2;
|
|
13561
14796
|
case 1:
|
|
13562
|
-
return x ^ y2 ^
|
|
14797
|
+
return x ^ y2 ^ z2;
|
|
13563
14798
|
case 2:
|
|
13564
|
-
return x & y2 ^ x &
|
|
14799
|
+
return x & y2 ^ x & z2 ^ y2 & z2;
|
|
13565
14800
|
case 3:
|
|
13566
|
-
return x ^ y2 ^
|
|
14801
|
+
return x ^ y2 ^ z2;
|
|
13567
14802
|
}
|
|
13568
14803
|
}
|
|
13569
14804
|
function ROTL(x, n) {
|
|
@@ -14612,7 +15847,7 @@ Si tienes alguna pregunta, no dudes en contactarnos respondiendo a este correo.
|
|
|
14612
15847
|
const extend_1 = __importDefault(extend);
|
|
14613
15848
|
const https_1 = require$$1;
|
|
14614
15849
|
const node_fetch_1 = __importDefault(browserExports);
|
|
14615
|
-
const querystring_1$1 = __importDefault(require$$
|
|
15850
|
+
const querystring_1$1 = __importDefault(require$$1$2);
|
|
14616
15851
|
const is_stream_1 = __importDefault(isStream_1);
|
|
14617
15852
|
const url_1 = require$$0$2;
|
|
14618
15853
|
const common_1 = common$1;
|
|
@@ -16290,11 +17525,11 @@ Content-Type: ${partContentType}\r
|
|
|
16290
17525
|
return n > 0 || n === i2 ? i2 : i2 - 1;
|
|
16291
17526
|
}
|
|
16292
17527
|
function coeffToString(a2) {
|
|
16293
|
-
var s2,
|
|
17528
|
+
var s2, z2, i2 = 1, j = a2.length, r = a2[0] + "";
|
|
16294
17529
|
for (; i2 < j; ) {
|
|
16295
17530
|
s2 = a2[i2++] + "";
|
|
16296
|
-
|
|
16297
|
-
for (;
|
|
17531
|
+
z2 = LOG_BASE - s2.length;
|
|
17532
|
+
for (; z2--; s2 = "0" + s2) ;
|
|
16298
17533
|
r += s2;
|
|
16299
17534
|
}
|
|
16300
17535
|
for (j = r.length; r.charCodeAt(--j) === 48; ) ;
|
|
@@ -16327,15 +17562,15 @@ Content-Type: ${partContentType}\r
|
|
|
16327
17562
|
function toExponential(str, e) {
|
|
16328
17563
|
return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
|
|
16329
17564
|
}
|
|
16330
|
-
function toFixedPoint(str, e,
|
|
17565
|
+
function toFixedPoint(str, e, z2) {
|
|
16331
17566
|
var len2, zs;
|
|
16332
17567
|
if (e < 0) {
|
|
16333
|
-
for (zs =
|
|
17568
|
+
for (zs = z2 + "."; ++e; zs += z2) ;
|
|
16334
17569
|
str = zs + str;
|
|
16335
17570
|
} else {
|
|
16336
17571
|
len2 = str.length;
|
|
16337
17572
|
if (++e > len2) {
|
|
16338
|
-
for (zs =
|
|
17573
|
+
for (zs = z2, e -= len2; --e; zs += z2) ;
|
|
16339
17574
|
str += zs;
|
|
16340
17575
|
} else if (e < len2) {
|
|
16341
17576
|
str = str.slice(0, e) + "." + str.slice(e);
|
|
@@ -16754,7 +17989,7 @@ Content-Type: ${partContentType}\r
|
|
|
16754
17989
|
exports3.isGoogleComputeEngine = isGoogleComputeEngine;
|
|
16755
17990
|
exports3.detectGCPResidency = detectGCPResidency;
|
|
16756
17991
|
const fs_1 = fs$4;
|
|
16757
|
-
const os_1 = require$$1$
|
|
17992
|
+
const os_1 = require$$1$3;
|
|
16758
17993
|
exports3.GCE_LINUX_BIOS_PATHS = {
|
|
16759
17994
|
BIOS_DATE: "/sys/class/dmi/id/bios_date",
|
|
16760
17995
|
BIOS_VENDOR: "/sys/class/dmi/id/bios_vendor"
|
|
@@ -16887,7 +18122,7 @@ Content-Type: ${partContentType}\r
|
|
|
16887
18122
|
exports3.setBackend = setBackend;
|
|
16888
18123
|
exports3.log = log;
|
|
16889
18124
|
const node_events_1 = require$$0$8;
|
|
16890
|
-
const process2 = __importStar2(require$$1$
|
|
18125
|
+
const process2 = __importStar2(require$$1$4);
|
|
16891
18126
|
const util2 = __importStar2(require$$2$1);
|
|
16892
18127
|
const colours_1 = colours;
|
|
16893
18128
|
var LogSeverity;
|
|
@@ -17959,7 +19194,7 @@ Content-Type: ${partContentType}\r
|
|
|
17959
19194
|
Object.defineProperty(oauth2client, "__esModule", { value: true });
|
|
17960
19195
|
oauth2client.OAuth2Client = oauth2client.ClientAuthentication = oauth2client.CertificateFormat = oauth2client.CodeChallengeMethod = void 0;
|
|
17961
19196
|
const gaxios_1$5 = src$2;
|
|
17962
|
-
const querystring$2 = require$$
|
|
19197
|
+
const querystring$2 = require$$1$2;
|
|
17963
19198
|
const stream$4 = require$$0$4;
|
|
17964
19199
|
const formatEcdsa = ecdsaSigFormatter;
|
|
17965
19200
|
const crypto_1$2 = requireCrypto();
|
|
@@ -19447,7 +20682,7 @@ Content-Type: ${partContentType}\r
|
|
|
19447
20682
|
Object.defineProperty(refreshclient, "__esModule", { value: true });
|
|
19448
20683
|
refreshclient.UserRefreshClient = refreshclient.USER_REFRESH_ACCOUNT_TYPE = void 0;
|
|
19449
20684
|
const oauth2client_1$1 = oauth2client;
|
|
19450
|
-
const querystring_1 = require$$
|
|
20685
|
+
const querystring_1 = require$$1$2;
|
|
19451
20686
|
refreshclient.USER_REFRESH_ACCOUNT_TYPE = "authorized_user";
|
|
19452
20687
|
class UserRefreshClient extends oauth2client_1$1.OAuth2Client {
|
|
19453
20688
|
constructor(optionsOrClientId, clientSecret, refreshToken, eagerRefreshThresholdMillis, forceRefreshOnFailure) {
|
|
@@ -19722,7 +20957,7 @@ Content-Type: ${partContentType}\r
|
|
|
19722
20957
|
Object.defineProperty(oauth2common, "__esModule", { value: true });
|
|
19723
20958
|
oauth2common.OAuthClientAuthHandler = void 0;
|
|
19724
20959
|
oauth2common.getErrorFromOAuthErrorResponse = getErrorFromOAuthErrorResponse;
|
|
19725
|
-
const querystring$1 = require$$
|
|
20960
|
+
const querystring$1 = require$$1$2;
|
|
19726
20961
|
const crypto_1$1 = requireCrypto();
|
|
19727
20962
|
const METHODS_SUPPORTING_REQUEST_BODY = ["PUT", "POST", "PATCH"];
|
|
19728
20963
|
class OAuthClientAuthHandler {
|
|
@@ -19868,7 +21103,7 @@ Content-Type: ${partContentType}\r
|
|
|
19868
21103
|
Object.defineProperty(stscredentials, "__esModule", { value: true });
|
|
19869
21104
|
stscredentials.StsCredentials = void 0;
|
|
19870
21105
|
const gaxios_1$1 = src$2;
|
|
19871
|
-
const querystring = require$$
|
|
21106
|
+
const querystring = require$$1$2;
|
|
19872
21107
|
const transporters_1 = transporters;
|
|
19873
21108
|
const oauth2common_1$1 = oauth2common;
|
|
19874
21109
|
class StsCredentials extends oauth2common_1$1.OAuthClientAuthHandler {
|
|
@@ -21474,7 +22709,7 @@ ${credentialScope}
|
|
|
21474
22709
|
const child_process_1 = require$$0$a;
|
|
21475
22710
|
const fs2 = fs$4;
|
|
21476
22711
|
const gcpMetadata2 = src$3;
|
|
21477
|
-
const os2 = require$$1$
|
|
22712
|
+
const os2 = require$$1$3;
|
|
21478
22713
|
const path2 = path$3;
|
|
21479
22714
|
const crypto_12 = requireCrypto();
|
|
21480
22715
|
const transporters_12 = transporters;
|
|
@@ -22838,7 +24073,7 @@ ${credentialScope}
|
|
|
22838
24073
|
}
|
|
22839
24074
|
function createAppleProvider(config) {
|
|
22840
24075
|
async function generateClientSecret() {
|
|
22841
|
-
return jwt.sign({}, config.privateKey, {
|
|
24076
|
+
return jwt$1.sign({}, config.privateKey, {
|
|
22842
24077
|
algorithm: "ES256",
|
|
22843
24078
|
keyid: config.keyId,
|
|
22844
24079
|
issuer: config.teamId,
|
|
@@ -23318,6 +24553,341 @@ ${credentialScope}
|
|
|
23318
24553
|
}
|
|
23319
24554
|
};
|
|
23320
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
|
+
}
|
|
23321
24891
|
function createCustomAuthAdapter(options2) {
|
|
23322
24892
|
const defaultCapabilities = {
|
|
23323
24893
|
hasBuiltInAuthRoutes: false,
|
|
@@ -23352,9 +24922,13 @@ ${credentialScope}
|
|
|
23352
24922
|
}
|
|
23353
24923
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
23354
24924
|
__proto__: null,
|
|
24925
|
+
apiKeyKeyGenerator,
|
|
23355
24926
|
configureJwt,
|
|
23356
24927
|
createAdapterAuthMiddleware,
|
|
23357
24928
|
createAdminRoutes,
|
|
24929
|
+
createApiKeyRateLimiter,
|
|
24930
|
+
createApiKeyRoutes,
|
|
24931
|
+
createApiKeyStore,
|
|
23358
24932
|
createAppleProvider,
|
|
23359
24933
|
createAuthMiddleware,
|
|
23360
24934
|
createAuthRoutes,
|
|
@@ -23380,11 +24954,15 @@ ${credentialScope}
|
|
|
23380
24954
|
getRefreshTokenExpiry,
|
|
23381
24955
|
hashPassword,
|
|
23382
24956
|
hashRefreshToken,
|
|
24957
|
+
httpMethodToOperation,
|
|
24958
|
+
isApiKeyToken,
|
|
24959
|
+
isOperationAllowed,
|
|
23383
24960
|
optionalAuth,
|
|
23384
24961
|
requireAdmin,
|
|
23385
24962
|
requireAuth,
|
|
23386
|
-
|
|
24963
|
+
resolveAuthHooks,
|
|
23387
24964
|
strictAuthLimiter,
|
|
24965
|
+
validateApiKey,
|
|
23388
24966
|
validatePasswordStrength,
|
|
23389
24967
|
verifyAccessToken,
|
|
23390
24968
|
verifyPassword
|
|
@@ -23909,6 +25487,409 @@ ${credentialScope}
|
|
|
23909
25487
|
};
|
|
23910
25488
|
}
|
|
23911
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();
|
|
23912
25893
|
function extractWildcardPath(c) {
|
|
23913
25894
|
const routePath = c.req.routePath;
|
|
23914
25895
|
const prefix = routePath.replace("/*", "");
|
|
@@ -23972,6 +25953,7 @@ ${credentialScope}
|
|
|
23972
25953
|
throw ApiError.notFound("File not found");
|
|
23973
25954
|
}
|
|
23974
25955
|
const filePath = decodeURIComponent(rawPath);
|
|
25956
|
+
const transformOpts = parseTransformOptions(c.req.query());
|
|
23975
25957
|
if (controller.getType() === "local") {
|
|
23976
25958
|
const localController = controller;
|
|
23977
25959
|
const {
|
|
@@ -23991,8 +25973,19 @@ ${credentialScope}
|
|
|
23991
25973
|
} catch {
|
|
23992
25974
|
}
|
|
23993
25975
|
}
|
|
23994
|
-
c.header("Content-Type", contentType);
|
|
23995
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);
|
|
23996
25989
|
return c.body(new Uint8Array(fileContent));
|
|
23997
25990
|
}
|
|
23998
25991
|
const {
|
|
@@ -24003,7 +25996,20 @@ ${credentialScope}
|
|
|
24003
25996
|
if (!fileObject) {
|
|
24004
25997
|
throw ApiError.notFound("File not found");
|
|
24005
25998
|
}
|
|
24006
|
-
|
|
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);
|
|
24007
26013
|
c.header("Cache-Control", "public, max-age=3600, immutable");
|
|
24008
26014
|
const buf = await fileObject.arrayBuffer();
|
|
24009
26015
|
return c.body(new Uint8Array(buf));
|
|
@@ -24099,6 +26105,14 @@ ${credentialScope}
|
|
|
24099
26105
|
message: "Folder created"
|
|
24100
26106
|
}, 201);
|
|
24101
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")));
|
|
24102
26116
|
return router;
|
|
24103
26117
|
}
|
|
24104
26118
|
const DEFAULT_STORAGE_ID = "(default)";
|
|
@@ -24322,6 +26336,13 @@ ${credentialScope}
|
|
|
24322
26336
|
} catch (e) {
|
|
24323
26337
|
}
|
|
24324
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
|
+
};
|
|
24325
26346
|
if (res.status === 401 && onUnauthorizedHandler) {
|
|
24326
26347
|
const retried = await onUnauthorizedHandler();
|
|
24327
26348
|
if (retried) {
|
|
@@ -24355,7 +26376,7 @@ ${credentialScope}
|
|
|
24355
26376
|
const method = init?.method || "GET";
|
|
24356
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.`;
|
|
24357
26378
|
}
|
|
24358
|
-
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"));
|
|
24359
26380
|
}
|
|
24360
26381
|
return retryBody;
|
|
24361
26382
|
}
|
|
@@ -24366,7 +26387,7 @@ ${credentialScope}
|
|
|
24366
26387
|
const method = init?.method || "GET";
|
|
24367
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.`;
|
|
24368
26389
|
}
|
|
24369
|
-
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"));
|
|
24370
26391
|
}
|
|
24371
26392
|
return body;
|
|
24372
26393
|
}
|
|
@@ -25028,118 +27049,6 @@ ${credentialScope}
|
|
|
25028
27049
|
toggleJob
|
|
25029
27050
|
};
|
|
25030
27051
|
}
|
|
25031
|
-
function mapOperator(op) {
|
|
25032
|
-
switch (op) {
|
|
25033
|
-
case "==":
|
|
25034
|
-
return "eq";
|
|
25035
|
-
case "!=":
|
|
25036
|
-
return "neq";
|
|
25037
|
-
case ">":
|
|
25038
|
-
return "gt";
|
|
25039
|
-
case ">=":
|
|
25040
|
-
return "gte";
|
|
25041
|
-
case "<":
|
|
25042
|
-
return "lt";
|
|
25043
|
-
case "<=":
|
|
25044
|
-
return "lte";
|
|
25045
|
-
case "array-contains":
|
|
25046
|
-
return "cs";
|
|
25047
|
-
case "array-contains-any":
|
|
25048
|
-
return "csa";
|
|
25049
|
-
case "not-in":
|
|
25050
|
-
return "nin";
|
|
25051
|
-
default:
|
|
25052
|
-
return op;
|
|
25053
|
-
}
|
|
25054
|
-
}
|
|
25055
|
-
class QueryBuilder {
|
|
25056
|
-
constructor(collection) {
|
|
25057
|
-
this.collection = collection;
|
|
25058
|
-
}
|
|
25059
|
-
params = {
|
|
25060
|
-
where: {}
|
|
25061
|
-
};
|
|
25062
|
-
/**
|
|
25063
|
-
* Add a filter condition to your query.
|
|
25064
|
-
* @example
|
|
25065
|
-
* client.collection('users').where('age', '>=', 18).find()
|
|
25066
|
-
*/
|
|
25067
|
-
where(column, operator, value) {
|
|
25068
|
-
if (!this.params.where) {
|
|
25069
|
-
this.params.where = {};
|
|
25070
|
-
}
|
|
25071
|
-
const mappedOp = mapOperator(operator);
|
|
25072
|
-
let formattedValue = value;
|
|
25073
|
-
if (Array.isArray(value) && ["in", "nin", "cs", "csa"].includes(mappedOp)) {
|
|
25074
|
-
formattedValue = `(${value.join(",")})`;
|
|
25075
|
-
} else if (value === null) {
|
|
25076
|
-
formattedValue = "null";
|
|
25077
|
-
}
|
|
25078
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
25079
|
-
return this;
|
|
25080
|
-
}
|
|
25081
|
-
/**
|
|
25082
|
-
* Order the results by a specific column.
|
|
25083
|
-
* @example
|
|
25084
|
-
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
25085
|
-
*/
|
|
25086
|
-
orderBy(column, ascending = "asc") {
|
|
25087
|
-
this.params.orderBy = `${column}:${ascending}`;
|
|
25088
|
-
return this;
|
|
25089
|
-
}
|
|
25090
|
-
/**
|
|
25091
|
-
* Limit the number of results returned.
|
|
25092
|
-
*/
|
|
25093
|
-
limit(count) {
|
|
25094
|
-
this.params.limit = count;
|
|
25095
|
-
return this;
|
|
25096
|
-
}
|
|
25097
|
-
/**
|
|
25098
|
-
* Skip the first N results.
|
|
25099
|
-
*/
|
|
25100
|
-
offset(count) {
|
|
25101
|
-
this.params.offset = count;
|
|
25102
|
-
return this;
|
|
25103
|
-
}
|
|
25104
|
-
/**
|
|
25105
|
-
* Set a free-text search string if supported by the backend.
|
|
25106
|
-
*/
|
|
25107
|
-
search(searchString) {
|
|
25108
|
-
this.params.searchString = searchString;
|
|
25109
|
-
return this;
|
|
25110
|
-
}
|
|
25111
|
-
/**
|
|
25112
|
-
* Include related entities in the response.
|
|
25113
|
-
* Relations will be populated with full entity data instead of just IDs.
|
|
25114
|
-
*
|
|
25115
|
-
* @param relations - Relation names to include, or "*" for all.
|
|
25116
|
-
* @example
|
|
25117
|
-
* // Include specific relations
|
|
25118
|
-
* client.data.posts.include("tags", "author").find()
|
|
25119
|
-
*
|
|
25120
|
-
* // Include all relations
|
|
25121
|
-
* client.data.posts.include("*").find()
|
|
25122
|
-
*/
|
|
25123
|
-
include(...relations) {
|
|
25124
|
-
this.params.include = relations;
|
|
25125
|
-
return this;
|
|
25126
|
-
}
|
|
25127
|
-
/**
|
|
25128
|
-
* Execute the find query and return the results.
|
|
25129
|
-
*/
|
|
25130
|
-
async find() {
|
|
25131
|
-
return this.collection.find(this.params);
|
|
25132
|
-
}
|
|
25133
|
-
/**
|
|
25134
|
-
* Listen to realtime updates matching this query.
|
|
25135
|
-
*/
|
|
25136
|
-
listen(onUpdate, onError) {
|
|
25137
|
-
if (!this.collection.listen) {
|
|
25138
|
-
throw new Error("Listen is only available when RebaseClient is configured with a websocketUrl.");
|
|
25139
|
-
}
|
|
25140
|
-
return this.collection.listen(this.params, onUpdate, onError);
|
|
25141
|
-
}
|
|
25142
|
-
}
|
|
25143
27052
|
function rowToEntity(row, slug) {
|
|
25144
27053
|
return {
|
|
25145
27054
|
id: row.id,
|
|
@@ -25734,7 +27643,7 @@ ${credentialScope}
|
|
|
25734
27643
|
const http = require$$0$6;
|
|
25735
27644
|
const https = require$$1;
|
|
25736
27645
|
const urllib$2 = require$$0$2;
|
|
25737
|
-
const zlib = require$$
|
|
27646
|
+
const zlib = require$$3;
|
|
25738
27647
|
const PassThrough$3 = require$$0$4.PassThrough;
|
|
25739
27648
|
const Cookies = cookies;
|
|
25740
27649
|
const packageData$7 = require$$9;
|
|
@@ -25969,9 +27878,9 @@ ${credentialScope}
|
|
|
25969
27878
|
const util2 = require$$5;
|
|
25970
27879
|
const fs2 = fs$4;
|
|
25971
27880
|
const nmfetch2 = fetchExports;
|
|
25972
|
-
const dns2 = require$$
|
|
27881
|
+
const dns2 = require$$4$1;
|
|
25973
27882
|
const net2 = require$$0$7;
|
|
25974
|
-
const os2 = require$$1$
|
|
27883
|
+
const os2 = require$$1$3;
|
|
25975
27884
|
const DNS_TTL = 5 * 60 * 1e3;
|
|
25976
27885
|
let networkInterfaces;
|
|
25977
27886
|
try {
|
|
@@ -32117,7 +34026,7 @@ ${credentialScope}
|
|
|
32117
34026
|
const packageData$6 = require$$9;
|
|
32118
34027
|
const MailMessage = mailMessage;
|
|
32119
34028
|
const net$1 = require$$0$7;
|
|
32120
|
-
const dns = require$$
|
|
34029
|
+
const dns = require$$4$1;
|
|
32121
34030
|
const crypto$3 = require$$0$5;
|
|
32122
34031
|
class Mail extends EventEmitter$5 {
|
|
32123
34032
|
constructor(transporter, options2, defaults) {
|
|
@@ -32553,7 +34462,7 @@ ${credentialScope}
|
|
|
32553
34462
|
const EventEmitter$4 = require$$0$9.EventEmitter;
|
|
32554
34463
|
const net = require$$0$7;
|
|
32555
34464
|
const tls = require$$1$1;
|
|
32556
|
-
const os = require$$1$
|
|
34465
|
+
const os = require$$1$3;
|
|
32557
34466
|
const crypto$2 = require$$0$5;
|
|
32558
34467
|
const DataStream = dataStream;
|
|
32559
34468
|
const PassThrough = require$$0$4.PassThrough;
|
|
@@ -36741,7 +38650,7 @@ ${credentialScope}
|
|
|
36741
38650
|
oauthProviders,
|
|
36742
38651
|
serviceKey,
|
|
36743
38652
|
hooks: config.hooks,
|
|
36744
|
-
|
|
38653
|
+
authHooks: safeAuthConfig.hooks
|
|
36745
38654
|
});
|
|
36746
38655
|
}
|
|
36747
38656
|
logger.info("Authentication initialized");
|
|
@@ -36886,7 +38795,7 @@ ${credentialScope}
|
|
|
36886
38795
|
oauthProviders,
|
|
36887
38796
|
serviceKey,
|
|
36888
38797
|
hooks: config.hooks,
|
|
36889
|
-
|
|
38798
|
+
authHooks: safeAuthConfig.hooks
|
|
36890
38799
|
});
|
|
36891
38800
|
}
|
|
36892
38801
|
if (authAdapter.createAuthRoutes) {
|
|
@@ -36908,6 +38817,21 @@ ${credentialScope}
|
|
|
36908
38817
|
}
|
|
36909
38818
|
}
|
|
36910
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
|
+
}
|
|
36911
38835
|
if (config.collectionsDir) {
|
|
36912
38836
|
if (process.env.NODE_ENV !== "production") {
|
|
36913
38837
|
const {
|
|
@@ -36958,15 +38882,20 @@ ${credentialScope}
|
|
|
36958
38882
|
dataRouter.use("/*", createAdapterAuthMiddleware({
|
|
36959
38883
|
adapter: authAdapter,
|
|
36960
38884
|
driver: defaultDriver,
|
|
36961
|
-
requireAuth: dataRequireAuth
|
|
38885
|
+
requireAuth: dataRequireAuth,
|
|
38886
|
+
apiKeyStore
|
|
36962
38887
|
}));
|
|
36963
38888
|
} else {
|
|
36964
38889
|
dataRouter.use("/*", createAuthMiddleware({
|
|
36965
38890
|
driver: defaultDriver,
|
|
36966
38891
|
requireAuth: dataRequireAuth,
|
|
36967
|
-
serviceKey
|
|
38892
|
+
serviceKey,
|
|
38893
|
+
apiKeyStore
|
|
36968
38894
|
}));
|
|
36969
38895
|
}
|
|
38896
|
+
if (apiKeyStore) {
|
|
38897
|
+
dataRouter.use("/*", createApiKeyRateLimiter());
|
|
38898
|
+
}
|
|
36970
38899
|
if (historyConfigResult && historyConfigResult.historyService) {
|
|
36971
38900
|
const historyRoutes = createHistoryRoutes({
|
|
36972
38901
|
historyService: historyConfigResult.historyService,
|
|
@@ -37060,13 +38989,15 @@ ${credentialScope}
|
|
|
37060
38989
|
functionsRouter.use("/*", createAdapterAuthMiddleware({
|
|
37061
38990
|
adapter: authAdapter,
|
|
37062
38991
|
driver: defaultDriver,
|
|
37063
|
-
requireAuth: functionsRequireAuth
|
|
38992
|
+
requireAuth: functionsRequireAuth,
|
|
38993
|
+
apiKeyStore
|
|
37064
38994
|
}));
|
|
37065
38995
|
} else {
|
|
37066
38996
|
functionsRouter.use("/*", createAuthMiddleware({
|
|
37067
38997
|
driver: defaultDriver,
|
|
37068
38998
|
requireAuth: functionsRequireAuth,
|
|
37069
|
-
serviceKey
|
|
38999
|
+
serviceKey,
|
|
39000
|
+
apiKeyStore
|
|
37070
39001
|
}));
|
|
37071
39002
|
}
|
|
37072
39003
|
const fnRoutes = createFunctionRoutes2(loadedFunctions);
|
|
@@ -47065,7 +48996,7 @@ spurious results.`);
|
|
|
47065
48996
|
return "undefined";
|
|
47066
48997
|
}
|
|
47067
48998
|
if (typeof obj === "string") {
|
|
47068
|
-
return
|
|
48999
|
+
return JSON.stringify(obj);
|
|
47069
49000
|
}
|
|
47070
49001
|
if (typeof obj === "number" || typeof obj === "boolean") {
|
|
47071
49002
|
return String(obj);
|
|
@@ -47096,7 +49027,7 @@ ${indent2}]`;
|
|
|
47096
49027
|
const kind = init.getKind();
|
|
47097
49028
|
const isCode = kind === tsMorph.SyntaxKind.ArrowFunction || kind === tsMorph.SyntaxKind.FunctionExpression || kind === tsMorph.SyntaxKind.Identifier || kind === tsMorph.SyntaxKind.CallExpression || kind === tsMorph.SyntaxKind.JsxElement;
|
|
47098
49029
|
if (isCode || name2 === "target" || name2 === "callbacks" || name2 === "permissions" || name2 === "securityRules") {
|
|
47099
|
-
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);
|
|
47100
49031
|
preservedProps.push(`${keyStr}: ${init.getText()}`);
|
|
47101
49032
|
}
|
|
47102
49033
|
}
|
|
@@ -47106,7 +49037,7 @@ ${indent2}]`;
|
|
|
47106
49037
|
}
|
|
47107
49038
|
if (keys2.length === 0 && preservedProps.length === 0) return "{}";
|
|
47108
49039
|
const props = keys2.map((key) => {
|
|
47109
|
-
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);
|
|
47110
49041
|
let childAstNode;
|
|
47111
49042
|
if (oldAstNode && typeof record[key] === "object" && record[key] !== null && !Array.isArray(record[key])) {
|
|
47112
49043
|
const oldProp = oldAstNode.getProperty((p) => "getName" in p && typeof p.getName === "function" && (p.getName() === key || p.getName() === `"${key}"` || p.getName() === `'${key}'`));
|
|
@@ -47148,7 +49079,7 @@ ${indent2}}`;
|
|
|
47148
49079
|
}
|
|
47149
49080
|
} else {
|
|
47150
49081
|
propsObj.addPropertyAssignment({
|
|
47151
|
-
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),
|
|
47152
49083
|
initializer: newInitializer
|
|
47153
49084
|
});
|
|
47154
49085
|
}
|
|
@@ -48219,7 +50150,7 @@ export default ${safeId}Collection;
|
|
|
48219
50150
|
const mod = await dynamicImport(fileUrl);
|
|
48220
50151
|
const exported = mod.default;
|
|
48221
50152
|
if (!exported) {
|
|
48222
|
-
|
|
50153
|
+
logger.warn(`[functions] ${file}: no default export. Skipping.`);
|
|
48223
50154
|
continue;
|
|
48224
50155
|
}
|
|
48225
50156
|
if (isHonoLike(exported)) {
|
|
@@ -48228,7 +50159,7 @@ export default ${safeId}Collection;
|
|
|
48228
50159
|
name: name2,
|
|
48229
50160
|
app: exported
|
|
48230
50161
|
});
|
|
48231
|
-
|
|
50162
|
+
logger.info(`⚡ Loaded function route: ${name2}`);
|
|
48232
50163
|
continue;
|
|
48233
50164
|
}
|
|
48234
50165
|
if (typeof exported === "function") {
|
|
@@ -48239,20 +50170,20 @@ export default ${safeId}Collection;
|
|
|
48239
50170
|
name: name2,
|
|
48240
50171
|
app: result
|
|
48241
50172
|
});
|
|
48242
|
-
|
|
50173
|
+
logger.info(`⚡ Loaded function route: ${name2}`);
|
|
48243
50174
|
continue;
|
|
48244
50175
|
}
|
|
48245
50176
|
}
|
|
48246
50177
|
const exportType = typeof exported;
|
|
48247
50178
|
const keys2 = exported && typeof exported === "object" ? Object.getOwnPropertyNames(Object.getPrototypeOf(exported)).slice(0, 10).join(", ") : "N/A";
|
|
48248
|
-
|
|
50179
|
+
logger.warn(`[functions] ${file}: default export is not a Hono app or factory. Skipping.
|
|
48249
50180
|
export type: ${exportType}${exported?.constructor?.name ? ` (${exported.constructor.name})` : ""}
|
|
48250
50181
|
prototype methods: ${keys2}
|
|
48251
50182
|
Hint: ensure the function exports a Hono app created with the same hono version as the server.
|
|
48252
50183
|
The loader checks for .fetch() and .routes — any Hono-compatible app will work.`);
|
|
48253
50184
|
} catch (err) {
|
|
48254
50185
|
const message = err instanceof Error ? err.message : String(err);
|
|
48255
|
-
|
|
50186
|
+
logger.error(`[functions] Failed to load ${file}: ${message}`);
|
|
48256
50187
|
}
|
|
48257
50188
|
}
|
|
48258
50189
|
}
|
|
@@ -48301,12 +50232,12 @@ export default ${safeId}Collection;
|
|
|
48301
50232
|
const mod = await dynamicImport(fileUrl);
|
|
48302
50233
|
const exported = mod.default;
|
|
48303
50234
|
if (!exported || typeof exported !== "object") {
|
|
48304
|
-
|
|
50235
|
+
logger.warn(`[cron] ${file}: no valid default export. Skipping.`);
|
|
48305
50236
|
continue;
|
|
48306
50237
|
}
|
|
48307
50238
|
const def = exported;
|
|
48308
50239
|
if (typeof def.schedule !== "string" || typeof def.handler !== "function") {
|
|
48309
|
-
|
|
50240
|
+
logger.warn(`[cron] ${file}: default export missing required 'schedule' or 'handler'. Skipping.`);
|
|
48310
50241
|
continue;
|
|
48311
50242
|
}
|
|
48312
50243
|
const id = path__namespace.basename(file, path__namespace.extname(file));
|
|
@@ -48322,10 +50253,10 @@ export default ${safeId}Collection;
|
|
|
48322
50253
|
id,
|
|
48323
50254
|
definition
|
|
48324
50255
|
});
|
|
48325
|
-
|
|
50256
|
+
logger.info(`⏰ Loaded cron job: ${id} (${definition.schedule})`);
|
|
48326
50257
|
} catch (err) {
|
|
48327
50258
|
const message = err instanceof Error ? err.message : String(err);
|
|
48328
|
-
|
|
50259
|
+
logger.error(`[cron] Failed to load ${file}: ${message}`);
|
|
48329
50260
|
}
|
|
48330
50261
|
}
|
|
48331
50262
|
}
|
|
@@ -48480,12 +50411,12 @@ export default ${safeId}Collection;
|
|
|
48480
50411
|
for (const loaded of loadedJobs) {
|
|
48481
50412
|
const validation = validateCronExpression(loaded.definition.schedule);
|
|
48482
50413
|
if (!validation.valid) {
|
|
48483
|
-
|
|
50414
|
+
logger.error(`[cron] Rejecting job "${loaded.id}": invalid schedule "${loaded.definition.schedule}" — ${validation.reason}`);
|
|
48484
50415
|
continue;
|
|
48485
50416
|
}
|
|
48486
50417
|
const existing = this.jobs.get(loaded.id);
|
|
48487
50418
|
if (existing) {
|
|
48488
|
-
|
|
50419
|
+
logger.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
|
|
48489
50420
|
this.stopJob(loaded.id);
|
|
48490
50421
|
}
|
|
48491
50422
|
const enabled = loaded.definition.enabled !== false;
|
|
@@ -48523,7 +50454,9 @@ export default ${safeId}Collection;
|
|
|
48523
50454
|
}
|
|
48524
50455
|
}
|
|
48525
50456
|
}).catch((err) => {
|
|
48526
|
-
|
|
50457
|
+
logger.warn("[cron] Failed to seed job stats from database", {
|
|
50458
|
+
error: err
|
|
50459
|
+
});
|
|
48527
50460
|
});
|
|
48528
50461
|
}
|
|
48529
50462
|
for (const [id, job] of this.jobs) {
|
|
@@ -48531,7 +50464,7 @@ export default ${safeId}Collection;
|
|
|
48531
50464
|
this.scheduleNext(id);
|
|
48532
50465
|
}
|
|
48533
50466
|
}
|
|
48534
|
-
|
|
50467
|
+
logger.info(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
|
|
48535
50468
|
}
|
|
48536
50469
|
/**
|
|
48537
50470
|
* Stop the scheduler and clear all timers.
|
|
@@ -48605,7 +50538,7 @@ export default ${safeId}Collection;
|
|
|
48605
50538
|
const job = this.jobs.get(id);
|
|
48606
50539
|
if (!job) return void 0;
|
|
48607
50540
|
if (job.executing) {
|
|
48608
|
-
|
|
50541
|
+
logger.warn(`[cron] Skipping manual trigger of "${id}" — already executing`);
|
|
48609
50542
|
const logEntry = {
|
|
48610
50543
|
jobId: id,
|
|
48611
50544
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -48649,7 +50582,7 @@ export default ${safeId}Collection;
|
|
|
48649
50582
|
const timer = setTimeout(async () => {
|
|
48650
50583
|
if (!job.enabled || !this.started) return;
|
|
48651
50584
|
if (job.executing) {
|
|
48652
|
-
|
|
50585
|
+
logger.warn(`[cron] Skipping scheduled run of "${id}" — still executing from previous run`);
|
|
48653
50586
|
this.scheduleNext(id);
|
|
48654
50587
|
return;
|
|
48655
50588
|
}
|
|
@@ -48663,7 +50596,9 @@ export default ${safeId}Collection;
|
|
|
48663
50596
|
}
|
|
48664
50597
|
job.timerId = timer;
|
|
48665
50598
|
} catch (err) {
|
|
48666
|
-
|
|
50599
|
+
logger.error(`[cron] Failed to schedule "${id}"`, {
|
|
50600
|
+
error: err
|
|
50601
|
+
});
|
|
48667
50602
|
job.state = "error";
|
|
48668
50603
|
job.lastError = err instanceof Error ? err.message : String(err);
|
|
48669
50604
|
}
|
|
@@ -48748,13 +50683,15 @@ export default ${safeId}Collection;
|
|
|
48748
50683
|
}
|
|
48749
50684
|
if (this.store) {
|
|
48750
50685
|
this.store.insertLog(logEntry).catch((persistErr) => {
|
|
48751
|
-
|
|
50686
|
+
logger.error(`[cron] Failed to persist log for "${job.id}"`, {
|
|
50687
|
+
error: persistErr
|
|
50688
|
+
});
|
|
48752
50689
|
});
|
|
48753
50690
|
}
|
|
48754
50691
|
if (success) {
|
|
48755
|
-
|
|
50692
|
+
logger.info(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
|
|
48756
50693
|
} else {
|
|
48757
|
-
|
|
50694
|
+
logger.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error2}`);
|
|
48758
50695
|
}
|
|
48759
50696
|
return logEntry;
|
|
48760
50697
|
}
|
|
@@ -48872,7 +50809,7 @@ export default ${safeId}Collection;
|
|
|
48872
50809
|
function createCronStore(driver) {
|
|
48873
50810
|
const admin = driver.admin;
|
|
48874
50811
|
if (!isSQLAdmin(admin)) {
|
|
48875
|
-
|
|
50812
|
+
logger.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
|
|
48876
50813
|
return void 0;
|
|
48877
50814
|
}
|
|
48878
50815
|
const exec = admin.executeSql.bind(admin);
|
|
@@ -48898,10 +50835,12 @@ export default ${safeId}Collection;
|
|
|
48898
50835
|
CREATE INDEX IF NOT EXISTS idx_cron_logs_job
|
|
48899
50836
|
ON ${TABLE}(job_id, started_at DESC)
|
|
48900
50837
|
`);
|
|
48901
|
-
|
|
50838
|
+
logger.info("✅ Cron logs table ready");
|
|
48902
50839
|
} catch (err) {
|
|
48903
|
-
|
|
48904
|
-
|
|
50840
|
+
logger.error("❌ Failed to create cron logs table", {
|
|
50841
|
+
error: err
|
|
50842
|
+
});
|
|
50843
|
+
logger.warn("⚠️ Continuing without cron log persistence.");
|
|
48905
50844
|
}
|
|
48906
50845
|
},
|
|
48907
50846
|
async insertLog(entry) {
|
|
@@ -48924,7 +50863,9 @@ export default ${safeId}Collection;
|
|
|
48924
50863
|
)
|
|
48925
50864
|
`);
|
|
48926
50865
|
} catch (err) {
|
|
48927
|
-
|
|
50866
|
+
logger.error(`[cron-store] Failed to persist log for "${entry.jobId}"`, {
|
|
50867
|
+
error: err
|
|
50868
|
+
});
|
|
48928
50869
|
}
|
|
48929
50870
|
},
|
|
48930
50871
|
async fetchLogs(jobId, limit = 50) {
|
|
@@ -48938,7 +50879,9 @@ export default ${safeId}Collection;
|
|
|
48938
50879
|
`);
|
|
48939
50880
|
return rows.map(rowToLogEntry);
|
|
48940
50881
|
} catch (err) {
|
|
48941
|
-
|
|
50882
|
+
logger.error(`[cron-store] Failed to fetch logs for "${jobId}"`, {
|
|
50883
|
+
error: err
|
|
50884
|
+
});
|
|
48942
50885
|
return [];
|
|
48943
50886
|
}
|
|
48944
50887
|
},
|
|
@@ -48962,7 +50905,9 @@ export default ${safeId}Collection;
|
|
|
48962
50905
|
});
|
|
48963
50906
|
}
|
|
48964
50907
|
} catch (err) {
|
|
48965
|
-
|
|
50908
|
+
logger.error("[cron-store] Failed to fetch job stats", {
|
|
50909
|
+
error: err
|
|
50910
|
+
});
|
|
48966
50911
|
}
|
|
48967
50912
|
return stats;
|
|
48968
50913
|
}
|
|
@@ -48993,8 +50938,8 @@ export default ${safeId}Collection;
|
|
|
48993
50938
|
indexFile = "index.html"
|
|
48994
50939
|
} = config;
|
|
48995
50940
|
if (!fs__namespace.existsSync(frontendPath)) {
|
|
48996
|
-
|
|
48997
|
-
|
|
50941
|
+
logger.warn(`⚠️ Frontend build path does not exist: ${frontendPath}`);
|
|
50942
|
+
logger.warn(" SPA serving is disabled. Build your frontend first.");
|
|
48998
50943
|
return;
|
|
48999
50944
|
}
|
|
49000
50945
|
app.use("/*", serveStatic.serveStatic({
|
|
@@ -49007,13 +50952,13 @@ export default ${safeId}Collection;
|
|
|
49007
50952
|
}
|
|
49008
50953
|
const indexPath = path__namespace.join(frontendPath, indexFile);
|
|
49009
50954
|
if (!fs__namespace.existsSync(indexPath)) {
|
|
49010
|
-
|
|
50955
|
+
logger.warn(`⚠️ Index file not found: ${indexPath}`);
|
|
49011
50956
|
return next();
|
|
49012
50957
|
}
|
|
49013
50958
|
const html = fs__namespace.readFileSync(indexPath, "utf-8");
|
|
49014
50959
|
return c.html(html);
|
|
49015
50960
|
});
|
|
49016
|
-
|
|
50961
|
+
logger.info(`✅ SPA serving enabled from: ${frontendPath}`);
|
|
49017
50962
|
}
|
|
49018
50963
|
const MAX_PORT_ATTEMPTS = 20;
|
|
49019
50964
|
const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
@@ -49021,6 +50966,19 @@ export default ${safeId}Collection;
|
|
|
49021
50966
|
const host = options2?.host ?? "0.0.0.0";
|
|
49022
50967
|
const maxAttempts = options2?.maxAttempts ?? MAX_PORT_ATTEMPTS;
|
|
49023
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
|
+
}
|
|
49024
50982
|
let affinityPort = null;
|
|
49025
50983
|
if (portFileDir) {
|
|
49026
50984
|
try {
|
|
@@ -49163,6 +51121,8 @@ export default ${safeId}Collection;
|
|
|
49163
51121
|
DB_POOL_MAX: stringType().default("20").transform(Number),
|
|
49164
51122
|
DB_POOL_IDLE_TIMEOUT: stringType().default("30000").transform(Number),
|
|
49165
51123
|
DB_POOL_CONNECT_TIMEOUT: stringType().default("10000").transform(Number),
|
|
51124
|
+
DATABASE_DIRECT_URL: stringType().url().optional(),
|
|
51125
|
+
DATABASE_READ_URL: stringType().url().optional(),
|
|
49166
51126
|
FORCE_LOCAL_STORAGE: optionalBoolString,
|
|
49167
51127
|
STORAGE_TYPE: enumType(["local", "s3"]).default("local"),
|
|
49168
51128
|
STORAGE_PATH: stringType().optional(),
|
|
@@ -49238,8 +51198,11 @@ export default ${safeId}Collection;
|
|
|
49238
51198
|
exports2.RestApiGenerator = RestApiGenerator;
|
|
49239
51199
|
exports2.S3StorageController = S3StorageController;
|
|
49240
51200
|
exports2.SMTPEmailService = SMTPEmailService;
|
|
51201
|
+
exports2.TransformCache = TransformCache;
|
|
51202
|
+
exports2.TusHandler = TusHandler;
|
|
49241
51203
|
exports2._resetRebaseMock = _resetRebaseMock;
|
|
49242
51204
|
exports2._setRebaseMock = _setRebaseMock;
|
|
51205
|
+
exports2.apiKeyKeyGenerator = apiKeyKeyGenerator;
|
|
49243
51206
|
exports2.authJwt = authJwt;
|
|
49244
51207
|
exports2.authRoles = authRoles;
|
|
49245
51208
|
exports2.authUid = authUid;
|
|
@@ -49248,6 +51211,9 @@ export default ${safeId}Collection;
|
|
|
49248
51211
|
exports2.configureLogLevel = configureLogLevel;
|
|
49249
51212
|
exports2.createAdapterAuthMiddleware = createAdapterAuthMiddleware;
|
|
49250
51213
|
exports2.createAdminRoutes = createAdminRoutes;
|
|
51214
|
+
exports2.createApiKeyRateLimiter = createApiKeyRateLimiter;
|
|
51215
|
+
exports2.createApiKeyRoutes = createApiKeyRoutes;
|
|
51216
|
+
exports2.createApiKeyStore = createApiKeyStore;
|
|
49251
51217
|
exports2.createAppleProvider = createAppleProvider;
|
|
49252
51218
|
exports2.createAuthMiddleware = createAuthMiddleware;
|
|
49253
51219
|
exports2.createAuthRoutes = createAuthRoutes;
|
|
@@ -49285,27 +51251,35 @@ export default ${safeId}Collection;
|
|
|
49285
51251
|
exports2.getWelcomeEmailTemplate = getWelcomeEmailTemplate;
|
|
49286
51252
|
exports2.hashPassword = hashPassword;
|
|
49287
51253
|
exports2.hashRefreshToken = hashRefreshToken;
|
|
51254
|
+
exports2.httpMethodToOperation = httpMethodToOperation;
|
|
49288
51255
|
exports2.initializeRebaseBackend = initializeRebaseBackend;
|
|
51256
|
+
exports2.isApiKeyToken = isApiKeyToken;
|
|
49289
51257
|
exports2.isAuthAdapter = isAuthAdapter;
|
|
49290
51258
|
exports2.isDatabaseAdapter = isDatabaseAdapter;
|
|
51259
|
+
exports2.isOperationAllowed = isOperationAllowed;
|
|
51260
|
+
exports2.isTransformableImage = isTransformableImage;
|
|
49291
51261
|
exports2.listenWithPortRetry = listenWithPortRetry;
|
|
49292
51262
|
exports2.loadCronJobsFromDirectory = loadCronJobsFromDirectory;
|
|
49293
51263
|
exports2.loadEnv = loadEnv;
|
|
49294
51264
|
exports2.loadFunctionsFromDirectory = loadFunctionsFromDirectory;
|
|
49295
51265
|
exports2.logger = logger;
|
|
49296
51266
|
exports2.optionalAuth = optionalAuth;
|
|
51267
|
+
exports2.parseTransformOptions = parseTransformOptions;
|
|
49297
51268
|
exports2.rebase = rebase;
|
|
49298
51269
|
exports2.requestLogger = requestLogger;
|
|
49299
51270
|
exports2.requireAdmin = requireAdmin;
|
|
49300
51271
|
exports2.requireAuth = requireAuth;
|
|
49301
51272
|
exports2.resetConsole = resetConsole;
|
|
49302
|
-
exports2.
|
|
51273
|
+
exports2.resolveAuthHooks = resolveAuthHooks;
|
|
49303
51274
|
exports2.serveSPA = serveSPA;
|
|
49304
51275
|
exports2.strictAuthLimiter = strictAuthLimiter;
|
|
51276
|
+
exports2.transformImage = transformImage;
|
|
51277
|
+
exports2.validateApiKey = validateApiKey;
|
|
49305
51278
|
exports2.validateCronExpression = validateCronExpression;
|
|
49306
51279
|
exports2.validatePasswordStrength = validatePasswordStrength;
|
|
49307
51280
|
exports2.verifyAccessToken = verifyAccessToken;
|
|
49308
51281
|
exports2.verifyPassword = verifyPassword;
|
|
51282
|
+
exports2.z = z;
|
|
49309
51283
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
49310
51284
|
});
|
|
49311
51285
|
//# sourceMappingURL=index.umd.js.map
|