@rpcbase/server 0.473.0 → 0.475.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/handler-BYVnU9H-.js +122 -0
- package/dist/{handler-ybYk2VTq.js → handler-CTL2iQCj.js} +4 -4
- package/dist/{handler-CTRE1McR.js → handler-xi0XKR-Y.js} +84 -101
- package/dist/{index-dlSIqvl2.js → index-Ckx0UHs6.js} +3 -3
- package/dist/index.js +1 -1
- package/dist/rts/api/changes/handler.d.ts +2 -2
- package/dist/rts/api/changes/handler.d.ts.map +1 -1
- package/dist/rts.js +2 -2
- package/dist/shared-Chfrv8o6.js +119 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/uploads/api/file-uploads/handler.d.ts.map +1 -1
- package/dist/uploads/api/file-uploads/middleware/rawBodyParser.d.ts +2 -1
- package/dist/uploads/api/file-uploads/middleware/rawBodyParser.d.ts.map +1 -1
- package/dist/uploads/api/file-uploads/shared.d.ts +2 -1
- package/dist/uploads/api/file-uploads/shared.d.ts.map +1 -1
- package/dist/uploads/api/files/handler.d.ts +4 -0
- package/dist/uploads/api/files/handler.d.ts.map +1 -0
- package/dist/uploads/api/files/handlers/deleteFile.d.ts +9 -0
- package/dist/uploads/api/files/handlers/deleteFile.d.ts.map +1 -0
- package/dist/uploads/api/files/handlers/getFile.d.ts +3 -0
- package/dist/uploads/api/files/handlers/getFile.d.ts.map +1 -0
- package/dist/uploads/api/files/handlers/getFile.test.d.ts +2 -0
- package/dist/uploads/api/files/handlers/getFile.test.d.ts.map +1 -0
- package/dist/uploads/api/files/index.d.ts +4 -0
- package/dist/uploads/api/files/index.d.ts.map +1 -0
- package/dist/uploads.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { getTenantFilesystemDb } from "@rpcbase/db";
|
|
2
|
+
import { ObjectId, GridFSBucket } from "mongodb";
|
|
3
|
+
import { g as getTenantId, c as getBucketName } from "./shared-Chfrv8o6.js";
|
|
4
|
+
const deleteFile = async (_payload, ctx) => {
|
|
5
|
+
const tenantId = getTenantId(ctx);
|
|
6
|
+
if (!tenantId) {
|
|
7
|
+
ctx.res.status(400);
|
|
8
|
+
return { ok: false, error: "tenant_missing" };
|
|
9
|
+
}
|
|
10
|
+
const fileIdRaw = String(ctx.req.params?.fileId ?? "").trim();
|
|
11
|
+
let fileObjectId;
|
|
12
|
+
try {
|
|
13
|
+
fileObjectId = new ObjectId(fileIdRaw);
|
|
14
|
+
} catch {
|
|
15
|
+
ctx.res.status(400);
|
|
16
|
+
return { ok: false, error: "invalid_file_id" };
|
|
17
|
+
}
|
|
18
|
+
const fsDb = await getTenantFilesystemDb(tenantId);
|
|
19
|
+
const nativeDb = fsDb.db;
|
|
20
|
+
if (!nativeDb) {
|
|
21
|
+
ctx.res.status(500);
|
|
22
|
+
return { ok: false, error: "filesystem_db_unavailable" };
|
|
23
|
+
}
|
|
24
|
+
const bucketName = getBucketName();
|
|
25
|
+
const bucket = new GridFSBucket(nativeDb, { bucketName });
|
|
26
|
+
try {
|
|
27
|
+
await bucket.delete(fileObjectId);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
+
if (!message.includes("FileNotFound")) {
|
|
31
|
+
ctx.res.status(500);
|
|
32
|
+
return { ok: false, error: "delete_failed" };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
ctx.res.status(204);
|
|
36
|
+
return { ok: true };
|
|
37
|
+
};
|
|
38
|
+
const resolveHeaderString = (value) => {
|
|
39
|
+
if (typeof value !== "string") return null;
|
|
40
|
+
const normalized = value.trim();
|
|
41
|
+
return normalized ? normalized : null;
|
|
42
|
+
};
|
|
43
|
+
const escapeHeaderFilename = (filename) => filename.replace(/[\\"]/g, "_");
|
|
44
|
+
const getFile = async (_payload, ctx) => {
|
|
45
|
+
const tenantId = getTenantId(ctx);
|
|
46
|
+
if (!tenantId) {
|
|
47
|
+
ctx.res.status(400).end();
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
const fileIdRaw = String(ctx.req.params?.fileId ?? "").trim();
|
|
51
|
+
let fileObjectId;
|
|
52
|
+
try {
|
|
53
|
+
fileObjectId = new ObjectId(fileIdRaw);
|
|
54
|
+
} catch {
|
|
55
|
+
ctx.res.status(400).end();
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
const fsDb = await getTenantFilesystemDb(tenantId);
|
|
59
|
+
const nativeDb = fsDb.db;
|
|
60
|
+
if (!nativeDb) {
|
|
61
|
+
ctx.res.status(500).end();
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
const bucketName = getBucketName();
|
|
65
|
+
const bucket = new GridFSBucket(nativeDb, { bucketName });
|
|
66
|
+
const [file] = await bucket.find({ _id: fileObjectId }).limit(1).toArray();
|
|
67
|
+
if (!file) {
|
|
68
|
+
ctx.res.status(404).end();
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
const mimeTypeFromMetadata = resolveHeaderString(file?.metadata?.mimeType);
|
|
72
|
+
const mimeType = mimeTypeFromMetadata ?? "application/octet-stream";
|
|
73
|
+
const filenameFromDb = resolveHeaderString(file?.filename);
|
|
74
|
+
const filename = filenameFromDb ?? fileIdRaw;
|
|
75
|
+
const filenameSafe = escapeHeaderFilename(filename);
|
|
76
|
+
const cacheControl = "private, max-age=0, must-revalidate";
|
|
77
|
+
const md5 = resolveHeaderString(file?.md5);
|
|
78
|
+
const uploadDate = file?.uploadDate instanceof Date ? file.uploadDate : null;
|
|
79
|
+
const etagValue = md5 ?? `${fileObjectId.toHexString()}-${String(file?.length ?? 0)}-${String(uploadDate?.getTime() ?? 0)}`;
|
|
80
|
+
const etag = md5 ? `"${etagValue}"` : `W/"${etagValue}"`;
|
|
81
|
+
const ifNoneMatch = resolveHeaderString(ctx.req.headers?.["if-none-match"]);
|
|
82
|
+
if (ifNoneMatch) {
|
|
83
|
+
const candidates = ifNoneMatch.split(",").map((value) => value.trim()).filter(Boolean);
|
|
84
|
+
if (candidates.includes("*") || candidates.includes(etag)) {
|
|
85
|
+
ctx.res.status(304);
|
|
86
|
+
ctx.res.setHeader("Cache-Control", cacheControl);
|
|
87
|
+
ctx.res.setHeader("ETag", etag);
|
|
88
|
+
ctx.res.end();
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
ctx.res.status(200);
|
|
93
|
+
ctx.res.setHeader("Content-Type", mimeType);
|
|
94
|
+
ctx.res.setHeader("Content-Length", String(file?.length ?? 0));
|
|
95
|
+
ctx.res.setHeader("Content-Disposition", `inline; filename="${filenameSafe}"`);
|
|
96
|
+
ctx.res.setHeader("Cache-Control", cacheControl);
|
|
97
|
+
ctx.res.setHeader("ETag", etag);
|
|
98
|
+
ctx.res.flushHeaders();
|
|
99
|
+
if (ctx.req.method === "HEAD") {
|
|
100
|
+
ctx.res.end();
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
const stream = bucket.openDownloadStream(fileObjectId);
|
|
104
|
+
stream.on("error", () => {
|
|
105
|
+
try {
|
|
106
|
+
ctx.res.destroy();
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
stream.pipe(ctx.res);
|
|
111
|
+
return {};
|
|
112
|
+
};
|
|
113
|
+
const Route = "/api/rb/files/:fileId";
|
|
114
|
+
const GetRoute = Route;
|
|
115
|
+
const DeleteRoute = Route;
|
|
116
|
+
const handler = (api) => {
|
|
117
|
+
api.get(GetRoute, getFile);
|
|
118
|
+
api.delete(DeleteRoute, deleteFile);
|
|
119
|
+
};
|
|
120
|
+
export {
|
|
121
|
+
handler as default
|
|
122
|
+
};
|
|
@@ -22,15 +22,15 @@ const getTenantId = (ctx) => {
|
|
|
22
22
|
const raw = ctx.req.query?.["rb-tenant-id"];
|
|
23
23
|
const queryTenantId = Array.isArray(raw) ? raw[0] : raw;
|
|
24
24
|
if (typeof queryTenantId === "string" && queryTenantId.trim()) return queryTenantId.trim();
|
|
25
|
-
const sessionTenantId = ctx.req.session?.user?.
|
|
25
|
+
const sessionTenantId = ctx.req.session?.user?.currentTenantId;
|
|
26
26
|
if (typeof sessionTenantId === "string" && sessionTenantId.trim()) return sessionTenantId.trim();
|
|
27
27
|
return null;
|
|
28
28
|
};
|
|
29
29
|
const ensureAuthorized = (ctx, tenantId) => {
|
|
30
30
|
const userId = ctx.req.session?.user?.id;
|
|
31
31
|
if (!userId) return null;
|
|
32
|
-
const signedInTenants = ctx.req.session?.user?.
|
|
33
|
-
const currentTenantId = ctx.req.session?.user?.
|
|
32
|
+
const signedInTenants = ctx.req.session?.user?.signedInTenants;
|
|
33
|
+
const currentTenantId = ctx.req.session?.user?.currentTenantId;
|
|
34
34
|
const hasTenantAccessFromList = Array.isArray(signedInTenants) && signedInTenants.includes(tenantId);
|
|
35
35
|
const normalizedCurrentTenantId = typeof currentTenantId === "string" ? currentTenantId.trim() : "";
|
|
36
36
|
const hasTenantAccessFromCurrent = Boolean(normalizedCurrentTenantId) && normalizedCurrentTenantId === tenantId;
|
|
@@ -41,7 +41,7 @@ const getModelCtx = (_ctx, tenantId) => ({
|
|
|
41
41
|
req: {
|
|
42
42
|
session: {
|
|
43
43
|
user: {
|
|
44
|
-
|
|
44
|
+
currentTenantId: tenantId
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -1,105 +1,8 @@
|
|
|
1
1
|
import { loadModel, getTenantFilesystemDb } from "@rpcbase/db";
|
|
2
2
|
import { GridFSBucket, ObjectId } from "mongodb";
|
|
3
|
-
import {
|
|
3
|
+
import { g as getTenantId, a as getModelCtx, b as getOwnershipSelector, e as ensureUploadIndexes, c as getBucketName, d as getUserId, f as getChunkSizeBytes, h as getSessionTtlMs, i as computeSha256Hex, t as toBufferPayload, n as normalizeSha256Hex, j as getMaxClientUploadBytesPerSecond, k as getRawBodyLimitBytes } from "./shared-Chfrv8o6.js";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
4
5
|
import { o as object, n as number, s as string, b as boolean, a as array, _ as _enum } from "./schemas-CyxqObur.js";
|
|
5
|
-
const DEFAULT_CHUNK_SIZE_BYTES = 5 * 1024 * 1024;
|
|
6
|
-
const MAX_CHUNK_SIZE_BYTES = 15 * 1024 * 1024;
|
|
7
|
-
const DEFAULT_SESSION_TTL_S = 60 * 60 * 24;
|
|
8
|
-
const ensuredIndexDbNames = /* @__PURE__ */ new Set();
|
|
9
|
-
const parseOptionalPositiveInt = (rawValue) => {
|
|
10
|
-
if (typeof rawValue !== "string") return null;
|
|
11
|
-
const normalized = rawValue.trim();
|
|
12
|
-
if (!normalized) return null;
|
|
13
|
-
const parsed = Number(normalized);
|
|
14
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return null;
|
|
15
|
-
return Math.floor(parsed);
|
|
16
|
-
};
|
|
17
|
-
const getChunkSizeBytes = () => {
|
|
18
|
-
const configured = parseOptionalPositiveInt(process.env.RB_UPLOAD_CHUNK_SIZE_BYTES);
|
|
19
|
-
const resolved = configured ?? DEFAULT_CHUNK_SIZE_BYTES;
|
|
20
|
-
return Math.min(MAX_CHUNK_SIZE_BYTES, resolved);
|
|
21
|
-
};
|
|
22
|
-
const getSessionTtlMs = () => {
|
|
23
|
-
const ttlSeconds = parseOptionalPositiveInt(process.env.RB_UPLOAD_SESSION_TTL_S) ?? DEFAULT_SESSION_TTL_S;
|
|
24
|
-
return ttlSeconds * 1e3;
|
|
25
|
-
};
|
|
26
|
-
const getRawBodyLimitBytes = (chunkSizeBytes) => chunkSizeBytes + 1024 * 1024;
|
|
27
|
-
const getBucketName = () => (process.env.RB_FILESYSTEM_BUCKET_NAME ?? "").trim() || "fs";
|
|
28
|
-
const getUserId = (ctx) => {
|
|
29
|
-
const raw = ctx.req.session?.user?.id;
|
|
30
|
-
if (typeof raw !== "string") return null;
|
|
31
|
-
const normalized = raw.trim();
|
|
32
|
-
return normalized ? normalized : null;
|
|
33
|
-
};
|
|
34
|
-
const getTenantId = (ctx) => {
|
|
35
|
-
const rawSession = ctx.req.session?.user?.current_tenant_id;
|
|
36
|
-
const sessionTenantId = typeof rawSession === "string" ? rawSession.trim() : "";
|
|
37
|
-
const userId = getUserId(ctx);
|
|
38
|
-
if (userId) return sessionTenantId || null;
|
|
39
|
-
if (sessionTenantId) return sessionTenantId;
|
|
40
|
-
const rawQuery = ctx.req.query?.["rb-tenant-id"];
|
|
41
|
-
const queryTenantId = Array.isArray(rawQuery) ? rawQuery[0] : rawQuery;
|
|
42
|
-
if (typeof queryTenantId !== "string") return null;
|
|
43
|
-
const normalized = queryTenantId.trim();
|
|
44
|
-
return normalized ? normalized : null;
|
|
45
|
-
};
|
|
46
|
-
const computeSha256Hex = (data) => createHash("sha256").update(data).digest("hex");
|
|
47
|
-
const normalizeSha256Hex = (value) => value.trim().toLowerCase();
|
|
48
|
-
const getModelCtx = (_ctx, tenantId) => ({
|
|
49
|
-
req: {
|
|
50
|
-
session: {
|
|
51
|
-
user: {
|
|
52
|
-
current_tenant_id: tenantId
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
const toBufferPayload = (payload) => {
|
|
58
|
-
if (Buffer.isBuffer(payload)) return payload;
|
|
59
|
-
if (payload instanceof Uint8Array) return Buffer.from(payload);
|
|
60
|
-
return null;
|
|
61
|
-
};
|
|
62
|
-
const ensureUploadIndexes = async (UploadSession, UploadChunk) => {
|
|
63
|
-
const dbName = String(UploadSession?.db?.name ?? "");
|
|
64
|
-
if (dbName && ensuredIndexDbNames.has(dbName)) return;
|
|
65
|
-
await Promise.all([
|
|
66
|
-
UploadSession.createIndexes(),
|
|
67
|
-
UploadChunk.createIndexes()
|
|
68
|
-
]);
|
|
69
|
-
if (dbName) ensuredIndexDbNames.add(dbName);
|
|
70
|
-
};
|
|
71
|
-
const normalizeUploadKey = (raw) => {
|
|
72
|
-
if (typeof raw !== "string") return null;
|
|
73
|
-
const normalized = raw.trim();
|
|
74
|
-
return normalized ? normalized : null;
|
|
75
|
-
};
|
|
76
|
-
const getUploadKeyHash = (ctx) => {
|
|
77
|
-
const uploadKey = normalizeUploadKey(ctx.req.get("X-Upload-Key"));
|
|
78
|
-
if (!uploadKey) return null;
|
|
79
|
-
return computeSha256Hex(Buffer.from(uploadKey));
|
|
80
|
-
};
|
|
81
|
-
const timingSafeEqualHex = (left, right) => {
|
|
82
|
-
if (left.length !== right.length) return false;
|
|
83
|
-
try {
|
|
84
|
-
return timingSafeEqual(Buffer.from(left, "hex"), Buffer.from(right, "hex"));
|
|
85
|
-
} catch {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
const getOwnershipSelector = (ctx, session) => {
|
|
90
|
-
if (session.userId) {
|
|
91
|
-
const userId = getUserId(ctx);
|
|
92
|
-
if (!userId || userId !== session.userId) return null;
|
|
93
|
-
return { userId: session.userId };
|
|
94
|
-
}
|
|
95
|
-
if (session.ownerKeyHash) {
|
|
96
|
-
const uploadKeyHash = getUploadKeyHash(ctx);
|
|
97
|
-
if (!uploadKeyHash) return null;
|
|
98
|
-
if (!timingSafeEqualHex(session.ownerKeyHash, uploadKeyHash)) return null;
|
|
99
|
-
return { ownerKeyHash: session.ownerKeyHash };
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
};
|
|
103
6
|
const waitForStreamFinished = async (stream) => new Promise((resolve, reject) => {
|
|
104
7
|
stream.once("finish", resolve);
|
|
105
8
|
stream.once("error", reject);
|
|
@@ -454,7 +357,8 @@ const uploadChunk = async (payload, ctx) => {
|
|
|
454
357
|
return { ok: true };
|
|
455
358
|
};
|
|
456
359
|
const rawBodyParser = ({
|
|
457
|
-
limitBytes
|
|
360
|
+
limitBytes,
|
|
361
|
+
maxClientBytesPerSecond
|
|
458
362
|
}) => {
|
|
459
363
|
return (req, res, next) => {
|
|
460
364
|
const contentType = typeof req?.headers?.["content-type"] === "string" ? String(req.headers["content-type"]) : "";
|
|
@@ -465,11 +369,18 @@ const rawBodyParser = ({
|
|
|
465
369
|
let total = 0;
|
|
466
370
|
const chunks = [];
|
|
467
371
|
let done = false;
|
|
372
|
+
let paused = false;
|
|
373
|
+
let throttleTimeout = null;
|
|
374
|
+
const rateBytesPerSecond = typeof maxClientBytesPerSecond === "number" && maxClientBytesPerSecond > 0 ? maxClientBytesPerSecond : null;
|
|
468
375
|
const cleanup = () => {
|
|
469
376
|
req.off("data", onData);
|
|
470
377
|
req.off("end", onEnd);
|
|
471
378
|
req.off("error", onError);
|
|
472
379
|
req.off("aborted", onAborted);
|
|
380
|
+
if (throttleTimeout) {
|
|
381
|
+
clearTimeout(throttleTimeout);
|
|
382
|
+
throttleTimeout = null;
|
|
383
|
+
}
|
|
473
384
|
};
|
|
474
385
|
const finish = (error) => {
|
|
475
386
|
if (done) return;
|
|
@@ -494,6 +405,24 @@ const rawBodyParser = ({
|
|
|
494
405
|
return;
|
|
495
406
|
}
|
|
496
407
|
chunks.push(buffer);
|
|
408
|
+
if (!rateBytesPerSecond) return;
|
|
409
|
+
const now = Date.now();
|
|
410
|
+
const clientKey = getClientKey(req);
|
|
411
|
+
const state = getClientRateState(clientKey, rateBytesPerSecond, now);
|
|
412
|
+
const waitMs = consumeRateBudget(state, buffer.length, rateBytesPerSecond, now);
|
|
413
|
+
if (waitMs > 0 && !paused) {
|
|
414
|
+
paused = true;
|
|
415
|
+
req.pause();
|
|
416
|
+
throttleTimeout = setTimeout(() => {
|
|
417
|
+
throttleTimeout = null;
|
|
418
|
+
paused = false;
|
|
419
|
+
if (done) return;
|
|
420
|
+
try {
|
|
421
|
+
req.resume();
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
}, waitMs);
|
|
425
|
+
}
|
|
497
426
|
};
|
|
498
427
|
const onEnd = () => finish();
|
|
499
428
|
const onError = (err) => finish(err);
|
|
@@ -504,9 +433,63 @@ const rawBodyParser = ({
|
|
|
504
433
|
req.on("aborted", onAborted);
|
|
505
434
|
};
|
|
506
435
|
};
|
|
436
|
+
const MAX_BURST_SECONDS = 1;
|
|
437
|
+
const STALE_CLIENT_MS = 15 * 60 * 1e3;
|
|
438
|
+
const clientRateStates = /* @__PURE__ */ new Map();
|
|
439
|
+
let lastCleanupMs = 0;
|
|
440
|
+
const getClientKey = (req) => {
|
|
441
|
+
const rawClientIp = typeof req?.clientIp === "string" ? req.clientIp : "";
|
|
442
|
+
if (rawClientIp.trim()) return rawClientIp.trim();
|
|
443
|
+
const rawIp = typeof req?.ip === "string" ? req.ip : "";
|
|
444
|
+
return rawIp.trim() || "unknown";
|
|
445
|
+
};
|
|
446
|
+
const maybeCleanupStates = (now) => {
|
|
447
|
+
if (now - lastCleanupMs < 6e4) return;
|
|
448
|
+
lastCleanupMs = now;
|
|
449
|
+
if (clientRateStates.size < 2e3) return;
|
|
450
|
+
for (const [key, state] of clientRateStates) {
|
|
451
|
+
if (now - state.lastSeenMs > STALE_CLIENT_MS) {
|
|
452
|
+
clientRateStates.delete(key);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
const getClientRateState = (key, rateBytesPerSecond, now) => {
|
|
457
|
+
maybeCleanupStates(now);
|
|
458
|
+
const capacity = rateBytesPerSecond * MAX_BURST_SECONDS;
|
|
459
|
+
const existing = clientRateStates.get(key);
|
|
460
|
+
if (existing) {
|
|
461
|
+
existing.lastSeenMs = now;
|
|
462
|
+
existing.tokens = Math.min(capacity, existing.tokens);
|
|
463
|
+
return existing;
|
|
464
|
+
}
|
|
465
|
+
const next = {
|
|
466
|
+
tokens: capacity,
|
|
467
|
+
lastRefillMs: now,
|
|
468
|
+
lastSeenMs: now
|
|
469
|
+
};
|
|
470
|
+
clientRateStates.set(key, next);
|
|
471
|
+
return next;
|
|
472
|
+
};
|
|
473
|
+
const consumeRateBudget = (state, bytes, rateBytesPerSecond, now) => {
|
|
474
|
+
const capacity = rateBytesPerSecond * MAX_BURST_SECONDS;
|
|
475
|
+
const elapsedMs = Math.max(0, now - state.lastRefillMs);
|
|
476
|
+
if (elapsedMs > 0) {
|
|
477
|
+
state.tokens = Math.min(capacity, state.tokens + elapsedMs * rateBytesPerSecond / 1e3);
|
|
478
|
+
state.lastRefillMs = now;
|
|
479
|
+
}
|
|
480
|
+
state.tokens -= bytes;
|
|
481
|
+
if (state.tokens >= 0) return 0;
|
|
482
|
+
return Math.ceil(-state.tokens / rateBytesPerSecond * 1e3);
|
|
483
|
+
};
|
|
507
484
|
const handler = (api) => {
|
|
508
485
|
const chunkSizeBytes = getChunkSizeBytes();
|
|
509
|
-
api.use(
|
|
486
|
+
api.use(
|
|
487
|
+
InitRoute,
|
|
488
|
+
rawBodyParser({
|
|
489
|
+
limitBytes: getRawBodyLimitBytes(chunkSizeBytes),
|
|
490
|
+
maxClientBytesPerSecond: getMaxClientUploadBytesPerSecond()
|
|
491
|
+
})
|
|
492
|
+
);
|
|
510
493
|
api.post(InitRoute, initUpload);
|
|
511
494
|
api.put(ChunkRoute, uploadChunk);
|
|
512
495
|
api.get(StatusRoute, getStatus);
|
|
@@ -137,8 +137,8 @@ const parseUpgradeMeta = async ({
|
|
|
137
137
|
if (!sessionUserId) {
|
|
138
138
|
throw new Error("Not signed in (missing session.user.id)");
|
|
139
139
|
}
|
|
140
|
-
const signedInTenants = sessionUser?.
|
|
141
|
-
const currentTenantId = sessionUser?.
|
|
140
|
+
const signedInTenants = sessionUser?.signedInTenants;
|
|
141
|
+
const currentTenantId = sessionUser?.currentTenantId;
|
|
142
142
|
if (Array.isArray(signedInTenants) && signedInTenants.length > 0) {
|
|
143
143
|
if (!signedInTenants.includes(tenantId)) {
|
|
144
144
|
throw new Error("Tenant not authorized for this session");
|
|
@@ -164,7 +164,7 @@ const getTenantModel = async (tenantId, modelName) => {
|
|
|
164
164
|
req: {
|
|
165
165
|
session: {
|
|
166
166
|
user: {
|
|
167
|
-
|
|
167
|
+
currentTenantId: tenantId
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
}
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { StrictMode, createElement } from "react";
|
|
|
18
18
|
import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
|
|
19
19
|
import { jsx } from "react/jsx-runtime";
|
|
20
20
|
import { createPath, matchRoutes, parsePath, createStaticRouter, StaticRouterProvider } from "@rpcbase/router";
|
|
21
|
-
import { i, n, r } from "./index-
|
|
21
|
+
import { i, n, r } from "./index-Ckx0UHs6.js";
|
|
22
22
|
function getDefaultExportFromCjs(x) {
|
|
23
23
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
24
24
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Api } from '../../../../../api/src';
|
|
2
2
|
type SessionUser = {
|
|
3
3
|
id?: string;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
currentTenantId?: string;
|
|
5
|
+
signedInTenants?: string[];
|
|
6
6
|
};
|
|
7
7
|
declare const _default: (api: Api<SessionUser>) => void;
|
|
8
8
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/rts/api/changes/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAOnD,KAAK,WAAW,GAAG;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/rts/api/changes/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAOnD,KAAK,WAAW,GAAG;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B,CAAA;yBA0He,KAAK,GAAG,CAAC,WAAW,CAAC;AAArC,wBAEC"}
|
package/dist/rts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i, n, r } from "./index-
|
|
1
|
+
import { i, n, r } from "./index-Ckx0UHs6.js";
|
|
2
2
|
const routes = Object.entries({
|
|
3
|
-
.../* @__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("./handler-
|
|
3
|
+
.../* @__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("./handler-CTL2iQCj.js") })
|
|
4
4
|
}).reduce((acc, [path, mod]) => {
|
|
5
5
|
acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
|
|
6
6
|
return acc;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
const DEFAULT_CHUNK_SIZE_BYTES = 5 * 1024 * 1024;
|
|
3
|
+
const MAX_CHUNK_SIZE_BYTES = 15 * 1024 * 1024;
|
|
4
|
+
const DEFAULT_MAX_CLIENT_BYTES_PER_SECOND = 10 * 1024 * 1024;
|
|
5
|
+
const DEFAULT_SESSION_TTL_S = 60 * 60 * 24;
|
|
6
|
+
const ensuredIndexDbNames = /* @__PURE__ */ new Set();
|
|
7
|
+
const parseOptionalPositiveInt = (rawValue) => {
|
|
8
|
+
if (typeof rawValue !== "string") return null;
|
|
9
|
+
const normalized = rawValue.trim();
|
|
10
|
+
if (!normalized) return null;
|
|
11
|
+
const parsed = Number(normalized);
|
|
12
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return null;
|
|
13
|
+
return Math.floor(parsed);
|
|
14
|
+
};
|
|
15
|
+
const getChunkSizeBytes = () => {
|
|
16
|
+
const configured = parseOptionalPositiveInt(process.env.RB_UPLOAD_CHUNK_SIZE_BYTES);
|
|
17
|
+
const resolved = configured ?? DEFAULT_CHUNK_SIZE_BYTES;
|
|
18
|
+
return Math.min(MAX_CHUNK_SIZE_BYTES, resolved);
|
|
19
|
+
};
|
|
20
|
+
const getMaxClientUploadBytesPerSecond = () => {
|
|
21
|
+
const configured = parseOptionalPositiveInt(process.env.RB_UPLOAD_MAX_CLIENT_BYTES_PER_SECOND);
|
|
22
|
+
return configured ?? DEFAULT_MAX_CLIENT_BYTES_PER_SECOND;
|
|
23
|
+
};
|
|
24
|
+
const getSessionTtlMs = () => {
|
|
25
|
+
const ttlSeconds = parseOptionalPositiveInt(process.env.RB_UPLOAD_SESSION_TTL_S) ?? DEFAULT_SESSION_TTL_S;
|
|
26
|
+
return ttlSeconds * 1e3;
|
|
27
|
+
};
|
|
28
|
+
const getRawBodyLimitBytes = (chunkSizeBytes) => chunkSizeBytes + 1024 * 1024;
|
|
29
|
+
const getBucketName = () => (process.env.RB_FILESYSTEM_BUCKET_NAME ?? "").trim() || "fs";
|
|
30
|
+
const getUserId = (ctx) => {
|
|
31
|
+
const raw = ctx.req.session?.user?.id;
|
|
32
|
+
if (typeof raw !== "string") return null;
|
|
33
|
+
const normalized = raw.trim();
|
|
34
|
+
return normalized ? normalized : null;
|
|
35
|
+
};
|
|
36
|
+
const getTenantId = (ctx) => {
|
|
37
|
+
const rawSession = ctx.req.session?.user?.currentTenantId;
|
|
38
|
+
const sessionTenantId = typeof rawSession === "string" ? rawSession.trim() : "";
|
|
39
|
+
const userId = getUserId(ctx);
|
|
40
|
+
const rawQuery = ctx.req.query?.["rb-tenant-id"];
|
|
41
|
+
const queryTenantId = Array.isArray(rawQuery) ? rawQuery[0] : rawQuery;
|
|
42
|
+
const queryValue = typeof queryTenantId === "string" && queryTenantId.trim() ? queryTenantId.trim() : null;
|
|
43
|
+
if (!userId && queryValue) return queryValue;
|
|
44
|
+
if (userId) return sessionTenantId || null;
|
|
45
|
+
if (sessionTenantId) return sessionTenantId;
|
|
46
|
+
return queryValue;
|
|
47
|
+
};
|
|
48
|
+
const computeSha256Hex = (data) => createHash("sha256").update(data).digest("hex");
|
|
49
|
+
const normalizeSha256Hex = (value) => value.trim().toLowerCase();
|
|
50
|
+
const getModelCtx = (_ctx, tenantId) => ({
|
|
51
|
+
req: {
|
|
52
|
+
session: {
|
|
53
|
+
user: {
|
|
54
|
+
currentTenantId: tenantId
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const toBufferPayload = (payload) => {
|
|
60
|
+
if (Buffer.isBuffer(payload)) return payload;
|
|
61
|
+
if (payload instanceof Uint8Array) return Buffer.from(payload);
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
64
|
+
const ensureUploadIndexes = async (UploadSession, UploadChunk) => {
|
|
65
|
+
const dbName = String(UploadSession?.db?.name ?? "");
|
|
66
|
+
if (dbName && ensuredIndexDbNames.has(dbName)) return;
|
|
67
|
+
await Promise.all([
|
|
68
|
+
UploadSession.createIndexes(),
|
|
69
|
+
UploadChunk.createIndexes()
|
|
70
|
+
]);
|
|
71
|
+
if (dbName) ensuredIndexDbNames.add(dbName);
|
|
72
|
+
};
|
|
73
|
+
const normalizeUploadKey = (raw) => {
|
|
74
|
+
if (typeof raw !== "string") return null;
|
|
75
|
+
const normalized = raw.trim();
|
|
76
|
+
return normalized ? normalized : null;
|
|
77
|
+
};
|
|
78
|
+
const getUploadKeyHash = (ctx) => {
|
|
79
|
+
const uploadKey = normalizeUploadKey(ctx.req.get("X-Upload-Key"));
|
|
80
|
+
if (!uploadKey) return null;
|
|
81
|
+
return computeSha256Hex(Buffer.from(uploadKey));
|
|
82
|
+
};
|
|
83
|
+
const timingSafeEqualHex = (left, right) => {
|
|
84
|
+
if (left.length !== right.length) return false;
|
|
85
|
+
try {
|
|
86
|
+
return timingSafeEqual(Buffer.from(left, "hex"), Buffer.from(right, "hex"));
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const getOwnershipSelector = (ctx, session) => {
|
|
92
|
+
if (session.userId) {
|
|
93
|
+
const userId = getUserId(ctx);
|
|
94
|
+
if (!userId || userId !== session.userId) return null;
|
|
95
|
+
return { userId: session.userId };
|
|
96
|
+
}
|
|
97
|
+
if (session.ownerKeyHash) {
|
|
98
|
+
const uploadKeyHash = getUploadKeyHash(ctx);
|
|
99
|
+
if (!uploadKeyHash) return null;
|
|
100
|
+
if (!timingSafeEqualHex(session.ownerKeyHash, uploadKeyHash)) return null;
|
|
101
|
+
return { ownerKeyHash: session.ownerKeyHash };
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
export {
|
|
106
|
+
getModelCtx as a,
|
|
107
|
+
getOwnershipSelector as b,
|
|
108
|
+
getBucketName as c,
|
|
109
|
+
getUserId as d,
|
|
110
|
+
ensureUploadIndexes as e,
|
|
111
|
+
getChunkSizeBytes as f,
|
|
112
|
+
getTenantId as g,
|
|
113
|
+
getSessionTtlMs as h,
|
|
114
|
+
computeSha256Hex as i,
|
|
115
|
+
getMaxClientUploadBytesPerSecond as j,
|
|
116
|
+
getRawBodyLimitBytes as k,
|
|
117
|
+
normalizeSha256Hex as n,
|
|
118
|
+
toBufferPayload as t
|
|
119
|
+
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/file-uploads/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAOlC,OAAO,
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/file-uploads/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAOlC,OAAO,EAA6E,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;yBAKtG,KAAK,GAAG,CAAC,WAAW,CAAC;AAArC,wBAcC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export declare const rawBodyParser: ({ limitBytes, }: {
|
|
1
|
+
export declare const rawBodyParser: ({ limitBytes, maxClientBytesPerSecond, }: {
|
|
2
2
|
limitBytes: number;
|
|
3
|
+
maxClientBytesPerSecond?: number | null;
|
|
3
4
|
}) => (req: any, res: any, next: any) => void;
|
|
4
5
|
//# sourceMappingURL=rawBodyParser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rawBodyParser.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/file-uploads/middleware/rawBodyParser.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,GAAI,
|
|
1
|
+
{"version":3,"file":"rawBodyParser.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/file-uploads/middleware/rawBodyParser.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,GAAI,0CAG3B;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxC,MACS,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG,SA6FtC,CAAA"}
|
|
@@ -3,13 +3,14 @@ import { IRBUploadChunk, IRBUploadSession, LoadModelCtx } from '../../../../../d
|
|
|
3
3
|
import { Model } from '../../../../../vite/node_modules/mongoose';
|
|
4
4
|
export type SessionUser = {
|
|
5
5
|
id?: string;
|
|
6
|
-
|
|
6
|
+
currentTenantId?: string;
|
|
7
7
|
};
|
|
8
8
|
export type UploadSessionDoc = IRBUploadSession;
|
|
9
9
|
export type UploadChunkDoc = Omit<IRBUploadChunk, "data"> & {
|
|
10
10
|
data: Buffer;
|
|
11
11
|
};
|
|
12
12
|
export declare const getChunkSizeBytes: () => number;
|
|
13
|
+
export declare const getMaxClientUploadBytesPerSecond: () => number | null;
|
|
13
14
|
export declare const getSessionTtlMs: () => number;
|
|
14
15
|
export declare const getRawBodyLimitBytes: (chunkSizeBytes: number) => number;
|
|
15
16
|
export declare const getBucketName: () => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/file-uploads/shared.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAGrC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/file-uploads/shared.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAGrC,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,CAAA;AAC/C,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAoB5E,eAAO,MAAM,iBAAiB,QAAO,MAIpC,CAAA;AAED,eAAO,MAAM,gCAAgC,QAAO,MAAM,GAAG,IAG5D,CAAA;AAED,eAAO,MAAM,eAAe,QAAO,MAGlC,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,gBAAgB,MAAM,KAAG,MAAsC,CAAA;AAEpG,eAAO,MAAM,aAAa,QAAO,MAAsE,CAAA;AAEvG,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,CAAC,WAAW,CAAC,KAAG,MAAM,GAAG,IAK1D,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,GAAG,CAAC,WAAW,CAAC,KAAG,MAAM,GAAG,IAgB5D,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,MAAyD,CAAA;AAEzG,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,MAAoC,CAAA;AAEvF,eAAO,MAAM,WAAW,GAAI,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,UAAU,MAAM,KAAG,YAQrE,CAAA;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,KAAG,MAAM,GAAG,IAI3D,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,eAAe,KAAK,CAAC,gBAAgB,CAAC,EACtC,aAAa,KAAK,CAAC,cAAc,CAAC,KACjC,OAAO,CAAC,IAAI,CAUd,CAAA;AAQD,eAAO,MAAM,gBAAgB,GAAI,KAAK,GAAG,CAAC,WAAW,CAAC,KAAG,MAAM,GAAG,IAIjE,CAAA;AAWD,eAAO,MAAM,oBAAoB,GAC/B,KAAK,GAAG,CAAC,WAAW,CAAC,EACrB,SAAS,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,cAAc,CAAC,KACzD;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAe/C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/files/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;yBAQlB,KAAK,GAAG;AAAxB,wBAGC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ApiHandler } from '../../../../../../api/src';
|
|
2
|
+
import { SessionUser } from '../../file-uploads/shared';
|
|
3
|
+
type DeleteResponsePayload = {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const deleteFile: ApiHandler<Record<string, never>, DeleteResponsePayload, SessionUser>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=deleteFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteFile.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/files/handlers/deleteFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAIzC,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAGxF,KAAK,qBAAqB,GAAG;IAC3B,EAAE,EAAE,OAAO,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,qBAAqB,EAAE,WAAW,CAyC5F,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/files/handlers/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAezC,eAAO,MAAM,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAmF5E,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFile.test.d.ts","sourceRoot":"","sources":["../../../../../src/uploads/api/files/handlers/getFile.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/files/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,0BAA0B,CAAA;AAE5C,eAAO,MAAM,QAAQ,0BAAQ,CAAA;AAC7B,eAAO,MAAM,WAAW,0BAAQ,CAAA"}
|
package/dist/uploads.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const routes = Object.entries({
|
|
2
|
-
.../* @__PURE__ */ Object.assign({ "./api/file-uploads/handler.ts": () => import("./handler-
|
|
2
|
+
.../* @__PURE__ */ Object.assign({ "./api/file-uploads/handler.ts": () => import("./handler-xi0XKR-Y.js"), "./api/files/handler.ts": () => import("./handler-BYVnU9H-.js") })
|
|
3
3
|
}).reduce((acc, [path, mod]) => {
|
|
4
4
|
acc[path.replace("./api/", "@rpcbase/server/uploads/api/")] = mod;
|
|
5
5
|
return acc;
|