@rpcbase/server 0.557.0 → 0.559.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/{email-DK8uUU4X.js → email-BbPlSkbt.js} +119 -119
- package/dist/{email-DK8uUU4X.js.map → email-BbPlSkbt.js.map} +1 -1
- package/dist/{handler-TcIyb69f.js → handler-BqoKvylN.js} +2 -2
- package/dist/{handler-TcIyb69f.js.map → handler-BqoKvylN.js.map} +1 -1
- package/dist/{handler-BUjHw_zG.js → handler-D-XdgeG_.js} +2 -2
- package/dist/{handler-BUjHw_zG.js.map → handler-D-XdgeG_.js.map} +1 -1
- package/dist/{handler-0rPClEv4.js → handler-D58VS91o.js} +2 -2
- package/dist/{handler-0rPClEv4.js.map → handler-D58VS91o.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/notifications.js +2 -2
- package/dist/{queryExecutor-Bzs0SJym.js → queryExecutor-DW7iAdRY.js} +27 -22
- package/dist/{queryExecutor-Bzs0SJym.js.map → queryExecutor-DW7iAdRY.js.map} +1 -1
- package/dist/rts/index.js +2 -2
- package/dist/rts/queryExecutor.d.ts.map +1 -1
- package/dist/{schemas-Cjdjgehl.js → schemas-Drf83ni9.js} +496 -204
- package/dist/schemas-Drf83ni9.js.map +1 -0
- package/dist/uploads.js +1 -1
- package/package.json +1 -1
- package/dist/schemas-Cjdjgehl.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { models, ZRBRtsChangeOp } from "@rpcbase/db";
|
|
2
2
|
import { buildAbilityFromSession } from "@rpcbase/db/acl";
|
|
3
|
-
import { o as object, a as array, s as string, n as number, _ as _enum, b as boolean } from "./schemas-
|
|
3
|
+
import { o as object, a as array, s as string, n as number, _ as _enum, b as boolean } from "./schemas-Drf83ni9.js";
|
|
4
4
|
const Route = "/api/rb/rts/changes";
|
|
5
5
|
const requestSchema = object({
|
|
6
6
|
sinceSeq: number().int().min(0).default(0),
|
|
@@ -144,4 +144,4 @@ const handler = (api) => {
|
|
|
144
144
|
export {
|
|
145
145
|
handler as default
|
|
146
146
|
};
|
|
147
|
-
//# sourceMappingURL=handler-
|
|
147
|
+
//# sourceMappingURL=handler-BqoKvylN.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-TcIyb69f.js","sources":["../src/rts/api/changes/index.ts","../src/rts/api/changes/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/rts/changes\"\n\nexport const requestSchema = z.object({\n sinceSeq: z.number().int().min(0).default(0),\n limit: z.number().int().min(1).max(5000).default(2000),\n modelNames: z.array(z.string().min(1)).optional(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n ok: z.boolean(),\n needsFullResync: z.boolean().optional(),\n earliestSeq: z.number().int().min(0).optional(),\n latestSeq: z.number().int().min(0),\n changes: z.array(z.object({\n seq: z.number().int().min(1),\n modelName: z.string().min(1),\n op: z.enum([\"delete\", \"reset_model\"]),\n docId: z.string().optional(),\n })),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models, ZRBRtsChangeOp, type IRBRtsChange, type IRBRtsCounter, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbilityFromSession, type AclSubjectType } from \"@rpcbase/db/acl\"\nimport type { Model } from \"mongoose\"\n\nimport * as Changes from \"./index\"\n\n\ntype SessionUser = {\n id?: string\n currentTenantId?: string\n signedInTenants?: string[]\n}\n\ntype RtsCounterDoc = IRBRtsCounter\ntype RtsChangeDoc = IRBRtsChange\n\nconst getTenantId = (ctx: Ctx<SessionUser>): string | null => {\n const sessionTenantId = ctx.req.session?.user?.currentTenantId\n if (typeof sessionTenantId === \"string\" && sessionTenantId.trim()) return sessionTenantId.trim()\n\n return null\n}\n\nconst ensureAuthorized = (ctx: Ctx<SessionUser>, tenantId: string): string | null => {\n const userId = ctx.req.session?.user?.id\n if (!userId) return null\n\n const currentTenantId = ctx.req.session?.user?.currentTenantId\n const normalizedCurrentTenantId = typeof currentTenantId === \"string\" ? currentTenantId.trim() : \"\"\n\n if (!normalizedCurrentTenantId || normalizedCurrentTenantId !== tenantId) return null\n return userId\n}\n\nconst getModelCtx = (_ctx: Ctx<SessionUser>, tenantId: string): LoadModelCtx => ({\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n})\n\nconst isRtsChangeRecord = (value: unknown): value is RtsChangeDoc => {\n if (!value || typeof value !== \"object\") return false\n const obj = value as Partial<RtsChangeDoc>\n const isOp = ZRBRtsChangeOp.safeParse(obj.op).success\n return typeof obj.seq === \"number\" && typeof obj.modelName === \"string\" && isOp\n}\n\nconst changesHandler: ApiHandler<Changes.RequestPayload, Changes.ResponsePayload, SessionUser> = async(\n payload,\n ctx,\n): Promise<Changes.ResponsePayload> => {\n const parsed = Changes.requestSchema.safeParse(payload ?? {})\n if (!parsed.success) {\n ctx.res.status(400)\n return { ok: false, latestSeq: 0, changes: [] }\n }\n\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, latestSeq: 0, changes: [] }\n }\n\n const userId = ensureAuthorized(ctx, tenantId)\n if (!userId) {\n ctx.res.status(401)\n return { ok: false, latestSeq: 0, changes: [] }\n }\n\n const ability = buildAbilityFromSession({ tenantId, session: ctx.req.session })\n\n const modelCtx = getModelCtx(ctx, tenantId)\n\n const [RtsChange, RtsCounter] = await Promise.all([\n models.get(\"RBRtsChange\", modelCtx) as Promise<Model<RtsChangeDoc>>,\n models.get(\"RBRtsCounter\", modelCtx) as Promise<Model<RtsCounterDoc>>,\n ])\n\n const counter = await RtsCounter.findOne({ _id: \"rts\" }, { seq: 1 }).lean()\n const latestSeq = Number(counter?.seq ?? 0) || 0\n\n const { sinceSeq, limit, modelNames } = parsed.data\n\n const requestedModelNames = Array.isArray(modelNames) && modelNames.length\n ? modelNames.map((m) => String(m)).filter(Boolean)\n : null\n\n const allowedModelNames = requestedModelNames\n ? requestedModelNames.filter((m) => ability.can(\"read\", m as AclSubjectType))\n : Array.from(\n new Set(\n (await RtsChange.distinct(\"modelName\"))\n .map((m) => String(m))\n .filter(Boolean)\n .filter((m) => ability.can(\"read\", m as AclSubjectType)),\n ),\n )\n\n let earliestSeq: number | undefined\n if (allowedModelNames.length) {\n const earliest = await RtsChange.findOne({ modelName: { $in: allowedModelNames } }, { seq: 1 }).sort({ seq: 1 }).lean()\n earliestSeq = earliest?.seq ? Number(earliest.seq) : undefined\n }\n\n const needsFullResync = typeof earliestSeq === \"number\" && sinceSeq < earliestSeq - 1\n\n const selector: Record<string, unknown> = { seq: { $gt: sinceSeq }, modelName: { $in: allowedModelNames } }\n\n const changes = await RtsChange\n .find(selector, { _id: 0, seq: 1, modelName: 1, op: 1, docId: 1 })\n .sort({ seq: 1 })\n .limit(limit)\n .lean()\n\n return {\n ok: true,\n needsFullResync: needsFullResync || undefined,\n earliestSeq,\n latestSeq,\n changes: Array.isArray(changes)\n ? changes\n .filter(isRtsChangeRecord)\n .filter((c) => ability.can(\"read\", c.modelName as AclSubjectType))\n .map((c) => ({\n seq: Number(c.seq),\n modelName: String(c.modelName),\n op: c.op,\n docId: c.docId ? String(c.docId) : undefined,\n }))\n : [],\n }\n}\n\nexport default (api: Api<SessionUser>) => {\n api.post(Changes.Route, changesHandler)\n}\n"],"names":["Route","requestSchema","z","sinceSeq","number","int","min","default","limit","max","modelNames","string","optional","ok","boolean","needsFullResync","earliestSeq","latestSeq","changes","seq","modelName","op","docId","getTenantId","ctx","sessionTenantId","req","session","user","currentTenantId","trim","ensureAuthorized","tenantId","userId","id","normalizedCurrentTenantId","getModelCtx","_ctx","isRtsChangeRecord","value","obj","isOp","ZRBRtsChangeOp","safeParse","success","changesHandler","payload","parsed","Changes","res","status","ability","buildAbilityFromSession","modelCtx","RtsChange","RtsCounter","Promise","all","models","get","counter","findOne","_id","lean","Number","data","requestedModelNames","Array","isArray","length","map","m","String","filter","Boolean","allowedModelNames","can","from","Set","distinct","earliest","$in","sort","undefined","selector","$gt","find","c","api","post"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handler-BqoKvylN.js","sources":["../src/rts/api/changes/index.ts","../src/rts/api/changes/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const Route = \"/api/rb/rts/changes\"\n\nexport const requestSchema = z.object({\n sinceSeq: z.number().int().min(0).default(0),\n limit: z.number().int().min(1).max(5000).default(2000),\n modelNames: z.array(z.string().min(1)).optional(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n ok: z.boolean(),\n needsFullResync: z.boolean().optional(),\n earliestSeq: z.number().int().min(0).optional(),\n latestSeq: z.number().int().min(0),\n changes: z.array(z.object({\n seq: z.number().int().min(1),\n modelName: z.string().min(1),\n op: z.enum([\"delete\", \"reset_model\"]),\n docId: z.string().optional(),\n })),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models, ZRBRtsChangeOp, type IRBRtsChange, type IRBRtsCounter, type LoadModelCtx } from \"@rpcbase/db\"\nimport { buildAbilityFromSession, type AclSubjectType } from \"@rpcbase/db/acl\"\nimport type { Model } from \"mongoose\"\n\nimport * as Changes from \"./index\"\n\n\ntype SessionUser = {\n id?: string\n currentTenantId?: string\n signedInTenants?: string[]\n}\n\ntype RtsCounterDoc = IRBRtsCounter\ntype RtsChangeDoc = IRBRtsChange\n\nconst getTenantId = (ctx: Ctx<SessionUser>): string | null => {\n const sessionTenantId = ctx.req.session?.user?.currentTenantId\n if (typeof sessionTenantId === \"string\" && sessionTenantId.trim()) return sessionTenantId.trim()\n\n return null\n}\n\nconst ensureAuthorized = (ctx: Ctx<SessionUser>, tenantId: string): string | null => {\n const userId = ctx.req.session?.user?.id\n if (!userId) return null\n\n const currentTenantId = ctx.req.session?.user?.currentTenantId\n const normalizedCurrentTenantId = typeof currentTenantId === \"string\" ? currentTenantId.trim() : \"\"\n\n if (!normalizedCurrentTenantId || normalizedCurrentTenantId !== tenantId) return null\n return userId\n}\n\nconst getModelCtx = (_ctx: Ctx<SessionUser>, tenantId: string): LoadModelCtx => ({\n req: {\n session: {\n user: {\n currentTenantId: tenantId,\n },\n },\n },\n})\n\nconst isRtsChangeRecord = (value: unknown): value is RtsChangeDoc => {\n if (!value || typeof value !== \"object\") return false\n const obj = value as Partial<RtsChangeDoc>\n const isOp = ZRBRtsChangeOp.safeParse(obj.op).success\n return typeof obj.seq === \"number\" && typeof obj.modelName === \"string\" && isOp\n}\n\nconst changesHandler: ApiHandler<Changes.RequestPayload, Changes.ResponsePayload, SessionUser> = async(\n payload,\n ctx,\n): Promise<Changes.ResponsePayload> => {\n const parsed = Changes.requestSchema.safeParse(payload ?? {})\n if (!parsed.success) {\n ctx.res.status(400)\n return { ok: false, latestSeq: 0, changes: [] }\n }\n\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, latestSeq: 0, changes: [] }\n }\n\n const userId = ensureAuthorized(ctx, tenantId)\n if (!userId) {\n ctx.res.status(401)\n return { ok: false, latestSeq: 0, changes: [] }\n }\n\n const ability = buildAbilityFromSession({ tenantId, session: ctx.req.session })\n\n const modelCtx = getModelCtx(ctx, tenantId)\n\n const [RtsChange, RtsCounter] = await Promise.all([\n models.get(\"RBRtsChange\", modelCtx) as Promise<Model<RtsChangeDoc>>,\n models.get(\"RBRtsCounter\", modelCtx) as Promise<Model<RtsCounterDoc>>,\n ])\n\n const counter = await RtsCounter.findOne({ _id: \"rts\" }, { seq: 1 }).lean()\n const latestSeq = Number(counter?.seq ?? 0) || 0\n\n const { sinceSeq, limit, modelNames } = parsed.data\n\n const requestedModelNames = Array.isArray(modelNames) && modelNames.length\n ? modelNames.map((m) => String(m)).filter(Boolean)\n : null\n\n const allowedModelNames = requestedModelNames\n ? requestedModelNames.filter((m) => ability.can(\"read\", m as AclSubjectType))\n : Array.from(\n new Set(\n (await RtsChange.distinct(\"modelName\"))\n .map((m) => String(m))\n .filter(Boolean)\n .filter((m) => ability.can(\"read\", m as AclSubjectType)),\n ),\n )\n\n let earliestSeq: number | undefined\n if (allowedModelNames.length) {\n const earliest = await RtsChange.findOne({ modelName: { $in: allowedModelNames } }, { seq: 1 }).sort({ seq: 1 }).lean()\n earliestSeq = earliest?.seq ? Number(earliest.seq) : undefined\n }\n\n const needsFullResync = typeof earliestSeq === \"number\" && sinceSeq < earliestSeq - 1\n\n const selector: Record<string, unknown> = { seq: { $gt: sinceSeq }, modelName: { $in: allowedModelNames } }\n\n const changes = await RtsChange\n .find(selector, { _id: 0, seq: 1, modelName: 1, op: 1, docId: 1 })\n .sort({ seq: 1 })\n .limit(limit)\n .lean()\n\n return {\n ok: true,\n needsFullResync: needsFullResync || undefined,\n earliestSeq,\n latestSeq,\n changes: Array.isArray(changes)\n ? changes\n .filter(isRtsChangeRecord)\n .filter((c) => ability.can(\"read\", c.modelName as AclSubjectType))\n .map((c) => ({\n seq: Number(c.seq),\n modelName: String(c.modelName),\n op: c.op,\n docId: c.docId ? String(c.docId) : undefined,\n }))\n : [],\n }\n}\n\nexport default (api: Api<SessionUser>) => {\n api.post(Changes.Route, changesHandler)\n}\n"],"names":["Route","requestSchema","z","sinceSeq","number","int","min","default","limit","max","modelNames","string","optional","ok","boolean","needsFullResync","earliestSeq","latestSeq","changes","seq","modelName","op","docId","getTenantId","ctx","sessionTenantId","req","session","user","currentTenantId","trim","ensureAuthorized","tenantId","userId","id","normalizedCurrentTenantId","getModelCtx","_ctx","isRtsChangeRecord","value","obj","isOp","ZRBRtsChangeOp","safeParse","success","changesHandler","payload","parsed","Changes","res","status","ability","buildAbilityFromSession","modelCtx","RtsChange","RtsCounter","Promise","all","models","get","counter","findOne","_id","lean","Number","data","requestedModelNames","Array","isArray","length","map","m","String","filter","Boolean","allowedModelNames","can","from","Set","distinct","earliest","$in","sort","undefined","selector","$gt","find","c","api","post"],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import { JSDOM } from "jsdom";
|
|
|
6
6
|
import createDOMPurify from "dompurify";
|
|
7
7
|
import { a as getTenantId, b as buildUploadsAbility, c as getUploadSessionAccessQuery, e as ensureUploadIndexes, g as getBucketName, d as getModelCtx, f as getUserId, h as getChunkSizeBytes, i as getSessionTtlMs, j as computeSha256Hex, t as toBufferPayload, n as normalizeSha256Hex, k as getMaxClientUploadBytesPerSecond, l as getRawBodyLimitBytes } from "./shared-CJrm9Wjp.js";
|
|
8
8
|
import { randomBytes } from "node:crypto";
|
|
9
|
-
import { o as object, n as number, b as boolean, s as string, a as array, _ as _enum } from "./schemas-
|
|
9
|
+
import { o as object, n as number, b as boolean, s as string, a as array, _ as _enum } from "./schemas-Drf83ni9.js";
|
|
10
10
|
const MAX_SVG_BYTES = 128 * 1024;
|
|
11
11
|
const window = new JSDOM("").window;
|
|
12
12
|
const DOMPurify = createDOMPurify(window);
|
|
@@ -983,4 +983,4 @@ const handler = (api) => {
|
|
|
983
983
|
export {
|
|
984
984
|
handler as default
|
|
985
985
|
};
|
|
986
|
-
//# sourceMappingURL=handler-
|
|
986
|
+
//# sourceMappingURL=handler-D-XdgeG_.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler-BUjHw_zG.js","sources":["../src/uploads/api/file-uploads/processors/sanitizeSvg.ts","../src/uploads/api/file-uploads/processors/index.ts","../src/uploads/api/file-uploads/handlers/completeUpload.ts","../src/uploads/api/file-uploads/handlers/getStatus.ts","../src/uploads/api/file-uploads/index.ts","../src/uploads/api/file-uploads/handlers/initUpload.ts","../src/uploads/api/file-uploads/handlers/uploadChunk.ts","../src/uploads/api/file-uploads/middleware/rawBodyParser.ts","../src/uploads/api/file-uploads/handler.ts"],"sourcesContent":["import { JSDOM } from \"jsdom\"\nimport createDOMPurify from \"dompurify\"\n\nimport type { UploadFileProcessor } from \"./index\"\n\n\nconst MAX_SVG_BYTES = 128 * 1024\n\nconst window = new JSDOM(\"\").window\nconst DOMPurify = createDOMPurify(window)\n\nconst normalizeForSniff = (raw: string): string => raw.replace(/^\\uFEFF/, \"\").trimStart()\n\nconst looksLikeSvgText = (text: string): boolean => {\n const normalized = normalizeForSniff(text)\n if (!normalized.startsWith(\"<\")) return false\n return /<svg(?:\\s|>)/i.test(normalized)\n}\n\nexport const looksLikeSvg = (sniff: Buffer): boolean => looksLikeSvgText(sniff.toString(\"utf8\"))\n\nexport const sanitizeSvg = (svg: string): string =>\n DOMPurify.sanitize(svg, {\n USE_PROFILES: { svg: true, svgFilters: true },\n })\n\nexport const sanitizeSvgProcessor: UploadFileProcessor = {\n id: \"sanitize-svg\",\n maxBytes: MAX_SVG_BYTES,\n match: ({ sniff }) => looksLikeSvg(sniff),\n process: (data): { data: Buffer; mimeType: string } => {\n if (data.length > MAX_SVG_BYTES) {\n throw new Error(\"svg_too_large\")\n }\n\n const svgText = data.toString(\"utf8\")\n if (!looksLikeSvgText(svgText)) {\n throw new Error(\"svg_invalid\")\n }\n\n const sanitized = sanitizeSvg(svgText)\n if (!sanitized.trim() || !looksLikeSvgText(sanitized)) {\n throw new Error(\"svg_sanitize_failed\")\n }\n\n const sanitizedBuffer = Buffer.from(sanitized, \"utf8\")\n if (sanitizedBuffer.length > MAX_SVG_BYTES) {\n throw new Error(\"svg_too_large\")\n }\n\n return { data: sanitizedBuffer, mimeType: \"image/svg+xml\" }\n },\n}\n\n","import { convertHeifToWebpProcessor } from \"./convertHeifToWebp\"\nimport { sanitizeSvgProcessor } from \"./sanitizeSvg\"\n\n\nexport type UploadFileProcessorContext = {\n filename: string\n clientMimeType: string\n totalSize: number\n sniff: Buffer\n}\n\nexport type UploadFileProcessorResult = {\n data: Buffer\n filename?: string\n mimeType?: string\n metadata?: Record<string, unknown>\n}\n\nexport type UploadFileProcessor = {\n id: string\n maxBytes: number\n match: (ctx: UploadFileProcessorContext) => boolean\n process: (data: Buffer, ctx: UploadFileProcessorContext) => Promise<UploadFileProcessorResult> | UploadFileProcessorResult\n}\n\nexport const uploadProcessors = Object.freeze([sanitizeSvgProcessor, convertHeifToWebpProcessor] satisfies UploadFileProcessor[])\n\nexport const getMaxUploadProcessorBytes = (): number =>\n uploadProcessors.reduce((max, processor) => Math.max(max, processor.maxBytes), 0)\n\nexport const selectUploadProcessors = (ctx: UploadFileProcessorContext): UploadFileProcessor[] =>\n uploadProcessors.filter((processor) => processor.match(ctx))\n\nexport const applyUploadProcessors = async (\n data: Buffer,\n ctx: Omit<UploadFileProcessorContext, \"sniff\" | \"totalSize\">,\n): Promise<{ data: Buffer; filename: string; mimeType: string; applied: string[]; metadata: Record<string, unknown> }> => {\n let currentData = data\n let currentFilename = ctx.filename\n let currentMimeType = ctx.clientMimeType\n const applied: string[] = []\n const metadata: Record<string, unknown> = {}\n\n for (const processor of uploadProcessors) {\n const processorCtx: UploadFileProcessorContext = {\n filename: currentFilename,\n clientMimeType: currentMimeType,\n totalSize: currentData.length,\n sniff: currentData,\n }\n\n if (!processor.match(processorCtx)) continue\n\n if (currentData.length > processor.maxBytes) {\n throw new Error(\"processor_input_too_large\")\n }\n\n const result = await processor.process(currentData, processorCtx)\n currentData = result.data\n if (typeof result.filename === \"string\" && result.filename.trim()) {\n currentFilename = result.filename.trim()\n }\n if (typeof result.mimeType === \"string\" && result.mimeType.trim()) {\n currentMimeType = result.mimeType.trim()\n }\n if (result.metadata && typeof result.metadata === \"object\") {\n Object.assign(metadata, result.metadata)\n }\n applied.push(processor.id)\n }\n\n return {\n data: currentData,\n filename: currentFilename,\n mimeType: currentMimeType,\n applied,\n metadata,\n }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb, models } from \"@rpcbase/db\"\nimport { GridFSBucket } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport { enqueueUploadPostProcessors } from \"../postProcessors\"\nimport { applyUploadProcessors, getMaxUploadProcessorBytes, selectUploadProcessors } from \"../processors\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n ensureUploadIndexes,\n getBucketName,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n} from \"../shared\"\n\n\nconst waitForStreamFinished = async (stream: NodeJS.WritableStream): Promise<void> => new Promise((resolve, reject) => {\n stream.once(\"finish\", resolve)\n stream.once(\"error\", reject)\n})\n\nconst writeToStream = async (stream: NodeJS.WritableStream, chunk: Buffer): Promise<void> => {\n const ok = stream.write(chunk)\n if (ok) return\n await new Promise<void>((resolve, reject) => {\n const onDrain = () => {\n cleanup()\n resolve()\n }\n\n const onError = (error: unknown) => {\n cleanup()\n reject(error)\n }\n\n const cleanup = () => {\n stream.off(\"drain\", onDrain)\n stream.off(\"error\", onError)\n }\n\n stream.on(\"drain\", onDrain)\n stream.on(\"error\", onError)\n })\n}\n\nconst abortUploadStream = async (stream: unknown): Promise<void> => {\n if (!stream) return\n if (typeof (stream as { abort?: unknown }).abort === \"function\") {\n try {\n await (stream as { abort: () => Promise<void> | void }).abort()\n return\n } catch {\n //\n }\n }\n try {\n ;(stream as { destroy?: () => void }).destroy?.()\n } catch {\n //\n }\n}\n\nexport const completeUpload: ApiHandler<Record<string, never>, Uploads.CompleteResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Uploads.CompleteResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const uploadId = String(ctx.req.params?.uploadId ?? \"\").trim()\n if (!uploadId) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_upload_id\" }\n }\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n if (!ability.can(\"update\", \"RBUploadSession\")) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const existing = await UploadSession.findOne({ $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"read\")] }).lean()\n if (!existing) {\n ctx.res.status(404)\n return { ok: false, error: \"not_found\" }\n }\n\n if (existing.status === \"done\" && existing.fileId) {\n return { ok: true, fileId: existing.fileId }\n }\n\n const locked = await UploadSession.findOneAndUpdate(\n { $and: [{ _id: uploadId }, { status: \"uploading\" }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"assembling\" }, $unset: { error: \"\" } },\n { returnDocument: \"after\" },\n ).lean()\n\n if (!locked) {\n ctx.res.status(409)\n return { ok: false, error: \"not_uploading\" }\n }\n\n await ensureUploadIndexes(UploadSession, UploadChunk)\n\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: \"filesystem_db_unavailable\" } },\n )\n ctx.res.status(500)\n return { ok: false, error: \"assembly_failed\" }\n }\n const bucketName = getBucketName()\n const bucket = new GridFSBucket(nativeDb, { bucketName })\n\n const lockedUserId = typeof locked.userId === \"string\" ? locked.userId : undefined\n const maxProcessorBytes = getMaxUploadProcessorBytes()\n const shouldBufferForProcessing = locked.totalSize <= maxProcessorBytes\n const declaredMimeType = locked.mimeType.trim().toLowerCase()\n const declaredSvg = declaredMimeType === \"image/svg+xml\" || locked.filename.trim().toLowerCase().endsWith(\".svg\")\n\n let uploadStream: NodeJS.WritableStream | null = null\n let finalFilename = locked.filename\n let finalMimeType = locked.mimeType\n let finalSize = locked.totalSize\n let inlineProcessors: string[] = []\n let finalMetadata: Record<string, unknown> = {\n uploadId,\n tenantId,\n mimeType: locked.mimeType,\n totalSize: locked.totalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n\n try {\n if (!shouldBufferForProcessing && declaredSvg) {\n throw new Error(\"svg_too_large\")\n }\n\n const cursor = UploadChunk.find({ uploadId }).sort({ index: 1 }).cursor() as unknown as AsyncIterable<UploadChunkDoc> & {\n close: () => Promise<void>\n }\n\n let expectedIndex = 0\n const chunks: Buffer[] = []\n let bufferedBytes = 0\n\n const pendingChunks: Buffer[] = []\n const sniffParts: Buffer[] = []\n let sniffBytes = 0\n\n try {\n for await (const chunkDoc of cursor) {\n if (chunkDoc.index !== expectedIndex) {\n throw new Error(\"missing_chunks\")\n }\n\n const chunk = chunkDoc.data\n\n if (shouldBufferForProcessing) {\n chunks.push(chunk)\n bufferedBytes += chunk.length\n } else if (!uploadStream) {\n pendingChunks.push(chunk)\n\n if (sniffBytes < maxProcessorBytes) {\n const slice = chunk.subarray(0, Math.min(chunk.length, maxProcessorBytes - sniffBytes))\n if (slice.length) {\n sniffParts.push(slice)\n sniffBytes += slice.length\n }\n }\n\n if (sniffBytes >= maxProcessorBytes) {\n const sniff = Buffer.concat(sniffParts, sniffBytes)\n const processors = selectUploadProcessors({\n filename: locked.filename,\n clientMimeType: locked.mimeType,\n totalSize: locked.totalSize,\n sniff,\n })\n\n if (processors.length) {\n throw new Error(processors.some((processor) => processor.id === \"sanitize-svg\") ? \"svg_too_large\" : \"processor_input_too_large\")\n }\n\n finalMetadata = {\n uploadId,\n tenantId,\n mimeType: locked.mimeType,\n totalSize: locked.totalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n uploadStream = bucket.openUploadStream(locked.filename, {\n metadata: finalMetadata,\n })\n\n for (const pending of pendingChunks) {\n await writeToStream(uploadStream, pending)\n }\n pendingChunks.length = 0\n }\n } else {\n await writeToStream(uploadStream, chunk)\n }\n\n expectedIndex += 1\n }\n } finally {\n try {\n await cursor.close()\n } catch {\n //\n }\n }\n\n if (expectedIndex !== locked.chunksTotal) {\n throw new Error(\"missing_chunks\")\n }\n\n if (shouldBufferForProcessing) {\n const assembled = Buffer.concat(chunks, bufferedBytes)\n const {\n data: processed,\n filename: processedFilename,\n mimeType: processedMimeType,\n applied,\n metadata: processedMetadata,\n } = await applyUploadProcessors(assembled, {\n filename: locked.filename,\n clientMimeType: locked.mimeType,\n })\n\n finalFilename = processedFilename\n finalMimeType = processedMimeType\n finalSize = processed.length\n inlineProcessors = applied\n finalMetadata = {\n uploadId,\n tenantId,\n mimeType: processedMimeType,\n totalSize: processed.length,\n ...(applied.length ? { processors: applied, sourceTotalSize: locked.totalSize } : {}),\n ...processedMetadata,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n uploadStream = bucket.openUploadStream(finalFilename, {\n metadata: finalMetadata,\n })\n\n const finished = waitForStreamFinished(uploadStream)\n uploadStream.end(processed)\n await finished\n } else {\n if (!uploadStream) {\n const sniff = Buffer.concat(sniffParts, sniffBytes)\n const processors = selectUploadProcessors({\n filename: locked.filename,\n clientMimeType: locked.mimeType,\n totalSize: locked.totalSize,\n sniff,\n })\n\n if (processors.length) {\n throw new Error(processors.some((processor) => processor.id === \"sanitize-svg\") ? \"svg_too_large\" : \"processor_input_too_large\")\n }\n\n finalMetadata = {\n uploadId,\n tenantId,\n mimeType: locked.mimeType,\n totalSize: locked.totalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n uploadStream = bucket.openUploadStream(locked.filename, {\n metadata: finalMetadata,\n })\n\n for (const pending of pendingChunks) {\n await writeToStream(uploadStream, pending)\n }\n pendingChunks.length = 0\n }\n\n const finished = waitForStreamFinished(uploadStream)\n uploadStream.end()\n await finished\n }\n\n const fileId = String((uploadStream as unknown as { id?: unknown }).id ?? \"\")\n if (!fileId) {\n throw new Error(\"missing_file_id\")\n }\n\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"done\", fileId, filename: finalFilename, mimeType: finalMimeType, totalSize: finalSize }, $unset: { error: \"\" } },\n )\n\n await enqueueUploadPostProcessors({\n tenantId,\n uploadId,\n fileId,\n filename: finalFilename,\n mimeType: finalMimeType,\n clientMimeType: locked.mimeType,\n totalSize: finalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n inlineProcessors,\n metadata: finalMetadata,\n }).catch((error) => {\n console.error(\"Upload post processor enqueue failed\", {\n tenantId,\n uploadId,\n fileId,\n error,\n })\n })\n\n try {\n await UploadChunk.deleteMany({ uploadId })\n } catch {\n //\n }\n\n return { ok: true, fileId }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n await abortUploadStream(uploadStream)\n\n if (message === \"missing_chunks\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"uploading\" } },\n )\n ctx.res.status(409)\n return { ok: false, error: \"missing_chunks\" }\n }\n\n if (message === \"svg_too_large\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(413)\n return { ok: false, error: message }\n }\n\n if (message === \"heif_too_large\" || message === \"processor_input_too_large\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(413)\n return { ok: false, error: message }\n }\n\n if (message.startsWith(\"heif_decode_unsupported\")) {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(415)\n return { ok: false, error: message }\n }\n\n if (message === \"svg_invalid\" || message === \"svg_sanitize_failed\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(400)\n return { ok: false, error: message }\n }\n\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n\n ctx.res.status(500)\n return { ok: false, error: \"assembly_failed\" }\n }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n} from \"../shared\"\n\n\nexport const getStatus: ApiHandler<Record<string, never>, Uploads.StatusResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Uploads.StatusResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const uploadId = String(ctx.req.params?.uploadId ?? \"\").trim()\n if (!uploadId) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_upload_id\" }\n }\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n if (!ability.can(\"read\", \"RBUploadSession\")) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const session = await UploadSession.findOne({ $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"read\")] }).lean()\n if (!session) {\n ctx.res.status(404)\n return { ok: false, error: \"not_found\" }\n }\n\n const receivedDocs = await UploadChunk.find(\n { uploadId },\n { index: 1, _id: 0 },\n ).sort({ index: 1 }).lean()\n\n const received = (receivedDocs as unknown as Array<{ index?: unknown }>)\n .map((doc) => (typeof doc.index === \"number\" ? doc.index : -1))\n .filter((n) => Number.isInteger(n) && n >= 0)\n\n return {\n ok: true,\n status: session.status,\n chunkSize: session.chunkSize,\n chunksTotal: session.chunksTotal,\n received,\n ...(session.fileId ? { fileId: session.fileId } : {}),\n }\n}\n","import { z } from \"zod\"\n\n\nexport const InitRoute = \"/api/rb/file-uploads\"\nexport const ChunkRoute = \"/api/rb/file-uploads/:uploadId/chunks/:index\"\nexport const StatusRoute = \"/api/rb/file-uploads/:uploadId/status\"\nexport const CompleteRoute = \"/api/rb/file-uploads/:uploadId/complete\"\n\nexport const initRequestSchema = z.object({\n filename: z.string().min(1),\n mimeType: z.string().min(1),\n isPublic: z.boolean().optional(),\n totalSize: z.number().int().min(1),\n})\n\nexport type InitRequestPayload = z.infer<typeof initRequestSchema>\n\nexport const initResponseSchema = z.object({\n ok: z.boolean(),\n error: z.string().optional(),\n uploadId: z.string().optional(),\n uploadKey: z.string().optional(),\n chunkSize: z.number().int().optional(),\n chunksTotal: z.number().int().optional(),\n})\n\nexport type InitResponsePayload = z.infer<typeof initResponseSchema>\n\nexport const statusResponseSchema = z.object({\n ok: z.boolean(),\n error: z.string().optional(),\n status: z.enum([\"uploading\", \"assembling\", \"done\", \"error\"]).optional(),\n chunkSize: z.number().int().optional(),\n chunksTotal: z.number().int().optional(),\n received: z.array(z.number().int().min(0)).optional(),\n fileId: z.string().optional(),\n})\n\nexport type StatusResponsePayload = z.infer<typeof statusResponseSchema>\n\nexport const completeResponseSchema = z.object({\n ok: z.boolean(),\n error: z.string().optional(),\n fileId: z.string().optional(),\n})\n\nexport type CompleteResponsePayload = z.infer<typeof completeResponseSchema>\n","import { randomBytes } from \"node:crypto\"\n\nimport { ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { ObjectId } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n computeSha256Hex,\n ensureUploadIndexes,\n getChunkSizeBytes,\n getModelCtx,\n getSessionTtlMs,\n getTenantId,\n getUserId,\n} from \"../shared\"\n\n\nexport const initUpload: ApiHandler<Uploads.InitRequestPayload, Uploads.InitResponsePayload, SessionUser> = async (\n payload,\n ctx,\n): Promise<Uploads.InitResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const userId = getUserId(ctx)\n\n const parsed = Uploads.initRequestSchema.safeParse(payload ?? {})\n if (!parsed.success) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_payload\" }\n }\n\n const chunkSize = getChunkSizeBytes()\n const { filename, mimeType, totalSize, isPublic } = parsed.data\n const chunksTotal = Math.ceil(totalSize / chunkSize)\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n await ensureUploadIndexes(UploadSession, UploadChunk)\n\n const uploadId = new ObjectId().toString()\n const now = Date.now()\n const expiresAt = new Date(now + getSessionTtlMs())\n\n const uploadKey = userId ? null : randomBytes(32).toString(\"base64url\")\n const ownerKeyHash = uploadKey ? computeSha256Hex(Buffer.from(uploadKey)) : undefined\n\n await UploadSession.create({\n _id: uploadId,\n ...(userId ? { userId } : {}),\n ...(ownerKeyHash ? { ownerKeyHash } : {}),\n filename,\n mimeType,\n ...(typeof isPublic === \"boolean\" ? { isPublic } : {}),\n totalSize,\n chunkSize,\n chunksTotal,\n status: \"uploading\",\n createdAt: new Date(now),\n expiresAt,\n })\n\n return {\n ok: true,\n uploadId,\n chunkSize,\n chunksTotal,\n ...(uploadKey ? { uploadKey } : {}),\n }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport type { Model } from \"mongoose\"\n\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n computeSha256Hex,\n ensureUploadIndexes,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n normalizeSha256Hex,\n toBufferPayload,\n} from \"../shared\"\n\n\ntype ChunkResponsePayload = {\n ok: boolean\n error?: string\n}\n\nexport const uploadChunk: ApiHandler<Buffer, ChunkResponsePayload, SessionUser> = async (\n payload,\n ctx,\n): Promise<ChunkResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const uploadId = String(ctx.req.params?.uploadId ?? \"\").trim()\n const indexRaw = String(ctx.req.params?.index ?? \"\").trim()\n const index = Number(indexRaw)\n\n if (!uploadId || !Number.isInteger(index) || index < 0) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_chunk_ref\" }\n }\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n if (!ability.can(\"update\", \"RBUploadSession\")) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const session = await UploadSession.findOne({ $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] }).lean()\n if (!session) {\n ctx.res.status(404)\n return { ok: false, error: \"not_found\" }\n }\n\n if (session.status !== \"uploading\") {\n ctx.res.status(409)\n return { ok: false, error: \"not_uploading\" }\n }\n\n if (index >= session.chunksTotal) {\n ctx.res.status(400)\n return { ok: false, error: \"index_out_of_range\" }\n }\n\n const data = toBufferPayload(payload)\n if (!data) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_body\" }\n }\n\n const expectedSize = index === session.chunksTotal - 1\n ? session.totalSize - session.chunkSize * (session.chunksTotal - 1)\n : session.chunkSize\n\n if (data.length > expectedSize) {\n ctx.res.status(413)\n return { ok: false, error: \"chunk_too_large\" }\n }\n\n if (data.length !== expectedSize) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_chunk_size\" }\n }\n\n const checksumHeader = ctx.req.get(\"X-Chunk-SHA256\")\n const sha256 = checksumHeader ? computeSha256Hex(data) : undefined\n\n if (checksumHeader) {\n const expectedSha256 = normalizeSha256Hex(checksumHeader)\n if (sha256 !== expectedSha256) {\n ctx.res.status(400)\n return { ok: false, error: \"checksum_mismatch\" }\n }\n }\n\n await ensureUploadIndexes(UploadSession, UploadChunk)\n\n await UploadChunk.updateOne(\n { uploadId, index },\n {\n $set: {\n uploadId,\n index,\n data,\n size: data.length,\n sha256,\n expiresAt: session.expiresAt,\n },\n $setOnInsert: {\n createdAt: new Date(),\n },\n },\n { upsert: true },\n )\n\n ctx.res.status(204)\n return { ok: true }\n}\n","export const rawBodyParser = ({\n limitBytes,\n maxClientBytesPerSecond,\n}: {\n limitBytes: number\n maxClientBytesPerSecond?: number | null\n}) => {\n return (req: any, res: any, next: any) => {\n const contentType = typeof req?.headers?.[\"content-type\"] === \"string\"\n ? String(req.headers[\"content-type\"])\n : \"\"\n\n if (!contentType.includes(\"application/octet-stream\")) {\n next()\n return\n }\n\n let total = 0\n const chunks: Buffer[] = []\n let done = false\n let paused = false\n let throttleTimeout: ReturnType<typeof setTimeout> | null = null\n\n const rateBytesPerSecond = typeof maxClientBytesPerSecond === \"number\" && maxClientBytesPerSecond > 0\n ? maxClientBytesPerSecond\n : null\n\n const cleanup = () => {\n req.off(\"data\", onData)\n req.off(\"end\", onEnd)\n req.off(\"error\", onError)\n req.off(\"aborted\", onAborted)\n if (throttleTimeout) {\n clearTimeout(throttleTimeout)\n throttleTimeout = null\n }\n }\n\n const finish = (error?: unknown) => {\n if (done) return\n done = true\n\n cleanup()\n\n if (error) {\n next(error)\n return\n }\n\n req.body = Buffer.concat(chunks, total)\n next()\n }\n\n const onData = (chunk: any) => {\n if (done) return\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n\n if (total > limitBytes) {\n done = true\n cleanup()\n req.destroy()\n res.status(413).json({ ok: false, error: \"chunk_too_large\" })\n return\n }\n\n chunks.push(buffer)\n\n if (!rateBytesPerSecond) return\n\n const now = Date.now()\n const clientKey = getClientKey(req)\n const state = getClientRateState(clientKey, rateBytesPerSecond, now)\n const waitMs = consumeRateBudget(state, buffer.length, rateBytesPerSecond, now)\n\n if (waitMs > 0 && !paused) {\n paused = true\n req.pause()\n throttleTimeout = setTimeout(() => {\n throttleTimeout = null\n paused = false\n if (done) return\n try {\n req.resume()\n } catch {\n //\n }\n }, waitMs)\n }\n }\n\n const onEnd = () => finish()\n const onError = (err: unknown) => finish(err)\n const onAborted = () => finish(new Error(\"request_aborted\"))\n\n req.on(\"data\", onData)\n req.on(\"end\", onEnd)\n req.on(\"error\", onError)\n req.on(\"aborted\", onAborted)\n }\n}\n\ntype ClientRateState = {\n tokens: number\n lastRefillMs: number\n lastSeenMs: number\n}\n\nconst MAX_BURST_SECONDS = 1\nconst STALE_CLIENT_MS = 15 * 60 * 1000\n\nconst clientRateStates = new Map<string, ClientRateState>()\nlet lastCleanupMs = 0\n\nconst getClientKey = (req: any): string => {\n const rawClientIp = typeof req?.clientIp === \"string\" ? req.clientIp : \"\"\n if (rawClientIp.trim()) return rawClientIp.trim()\n const rawIp = typeof req?.ip === \"string\" ? req.ip : \"\"\n return rawIp.trim() || \"unknown\"\n}\n\nconst maybeCleanupStates = (now: number) => {\n if (now - lastCleanupMs < 60_000) return\n lastCleanupMs = now\n\n if (clientRateStates.size < 2000) return\n\n for (const [key, state] of clientRateStates) {\n if (now - state.lastSeenMs > STALE_CLIENT_MS) {\n clientRateStates.delete(key)\n }\n }\n}\n\nconst getClientRateState = (key: string, rateBytesPerSecond: number, now: number): ClientRateState => {\n maybeCleanupStates(now)\n\n const capacity = rateBytesPerSecond * MAX_BURST_SECONDS\n const existing = clientRateStates.get(key)\n if (existing) {\n existing.lastSeenMs = now\n existing.tokens = Math.min(capacity, existing.tokens)\n return existing\n }\n\n const next: ClientRateState = {\n tokens: capacity,\n lastRefillMs: now,\n lastSeenMs: now,\n }\n clientRateStates.set(key, next)\n return next\n}\n\nconst consumeRateBudget = (\n state: ClientRateState,\n bytes: number,\n rateBytesPerSecond: number,\n now: number,\n): number => {\n const capacity = rateBytesPerSecond * MAX_BURST_SECONDS\n const elapsedMs = Math.max(0, now - state.lastRefillMs)\n\n if (elapsedMs > 0) {\n state.tokens = Math.min(capacity, state.tokens + (elapsedMs * rateBytesPerSecond) / 1000)\n state.lastRefillMs = now\n }\n\n state.tokens -= bytes\n\n if (state.tokens >= 0) return 0\n return Math.ceil((-state.tokens / rateBytesPerSecond) * 1000)\n}\n","import { Api } from \"@rpcbase/api\"\n\nimport { completeUpload } from \"./handlers/completeUpload\"\nimport { getStatus } from \"./handlers/getStatus\"\nimport { initUpload } from \"./handlers/initUpload\"\nimport { uploadChunk } from \"./handlers/uploadChunk\"\nimport { rawBodyParser } from \"./middleware/rawBodyParser\"\nimport { getChunkSizeBytes, getMaxClientUploadBytesPerSecond, getRawBodyLimitBytes, type SessionUser } from \"./shared\"\n\nimport * as Uploads from \"./index\"\n\n\nexport default (api: Api<SessionUser>) => {\n const chunkSizeBytes = getChunkSizeBytes()\n api.use(\n Uploads.InitRoute,\n rawBodyParser({\n limitBytes: getRawBodyLimitBytes(chunkSizeBytes),\n maxClientBytesPerSecond: getMaxClientUploadBytesPerSecond(),\n }),\n )\n\n api.post(Uploads.InitRoute, initUpload)\n api.put(Uploads.ChunkRoute, uploadChunk)\n api.get(Uploads.StatusRoute, getStatus)\n api.post(Uploads.CompleteRoute, completeUpload)\n}\n"],"names":["MAX_SVG_BYTES","window","JSDOM","DOMPurify","createDOMPurify","normalizeForSniff","raw","replace","trimStart","looksLikeSvgText","text","normalized","startsWith","test","looksLikeSvg","sniff","toString","sanitizeSvg","svg","sanitize","USE_PROFILES","svgFilters","sanitizeSvgProcessor","id","maxBytes","match","process","data","length","Error","svgText","sanitized","trim","sanitizedBuffer","Buffer","from","mimeType","uploadProcessors","Object","freeze","convertHeifToWebpProcessor","getMaxUploadProcessorBytes","reduce","max","processor","Math","selectUploadProcessors","ctx","filter","applyUploadProcessors","currentData","currentFilename","filename","currentMimeType","clientMimeType","applied","metadata","processorCtx","totalSize","result","assign","push","waitForStreamFinished","stream","Promise","resolve","reject","once","writeToStream","chunk","ok","write","onDrain","cleanup","onError","error","off","on","abortUploadStream","abort","destroy","completeUpload","_payload","tenantId","getTenantId","res","status","uploadId","String","req","params","ability","buildUploadsAbility","modelCtx","getModelCtx","UploadSession","UploadChunk","all","models","get","can","existing","findOne","$and","_id","getUploadSessionAccessQuery","lean","fileId","locked","findOneAndUpdate","$set","$unset","returnDocument","ensureUploadIndexes","fsDb","getTenantFilesystemDb","nativeDb","db","updateOne","bucketName","getBucketName","bucket","GridFSBucket","lockedUserId","userId","undefined","maxProcessorBytes","shouldBufferForProcessing","declaredMimeType","toLowerCase","declaredSvg","endsWith","uploadStream","finalFilename","finalMimeType","finalSize","inlineProcessors","finalMetadata","isPublic","ownerKeyHash","cursor","find","sort","index","expectedIndex","chunks","bufferedBytes","pendingChunks","sniffParts","sniffBytes","chunkDoc","slice","subarray","min","concat","processors","some","openUploadStream","pending","close","chunksTotal","assembled","processed","processedFilename","processedMimeType","processedMetadata","sourceTotalSize","finished","end","enqueueUploadPostProcessors","catch","console","deleteMany","message","getStatus","session","receivedDocs","received","map","doc","n","Number","isInteger","chunkSize","InitRoute","ChunkRoute","StatusRoute","CompleteRoute","initRequestSchema","z","string","boolean","optional","number","int","uploadKey","initUpload","payload","getUserId","parsed","Uploads","safeParse","success","getChunkSizeBytes","ceil","ObjectId","now","Date","expiresAt","getSessionTtlMs","randomBytes","computeSha256Hex","create","createdAt","uploadChunk","indexRaw","toBufferPayload","expectedSize","checksumHeader","sha256","expectedSha256","normalizeSha256Hex","size","$setOnInsert","upsert","rawBodyParser","limitBytes","maxClientBytesPerSecond","next","contentType","headers","includes","total","done","paused","throttleTimeout","rateBytesPerSecond","onData","onEnd","onAborted","clearTimeout","finish","body","buffer","isBuffer","json","clientKey","getClientKey","state","getClientRateState","waitMs","consumeRateBudget","pause","setTimeout","resume","err","MAX_BURST_SECONDS","STALE_CLIENT_MS","clientRateStates","Map","lastCleanupMs","rawClientIp","clientIp","rawIp","ip","maybeCleanupStates","key","lastSeenMs","delete","capacity","tokens","lastRefillMs","set","bytes","elapsedMs","api","chunkSizeBytes","use","getRawBodyLimitBytes","getMaxClientUploadBytesPerSecond","post","put"],"mappings":";;;;;;;;;AAMA,MAAMA,gBAAgB,MAAM;AAE5B,MAAMC,SAAS,IAAIC,MAAM,EAAE,EAAED;AAC7B,MAAME,YAAYC,gBAAgBH,MAAM;AAExC,MAAMI,oBAAoBA,CAACC,QAAwBA,IAAIC,QAAQ,WAAW,EAAE,EAAEC,UAAAA;AAE9E,MAAMC,mBAAmBA,CAACC,SAA0B;AAClD,QAAMC,aAAaN,kBAAkBK,IAAI;AACzC,MAAI,CAACC,WAAWC,WAAW,GAAG,EAAG,QAAO;AACxC,SAAO,gBAAgBC,KAAKF,UAAU;AACxC;AAEO,MAAMG,eAAeA,CAACC,UAA2BN,iBAAiBM,MAAMC,SAAS,MAAM,CAAC;AAExF,MAAMC,cAAcA,CAACC,QAC1Bf,UAAUgB,SAASD,KAAK;AAAA,EACtBE,cAAc;AAAA,IAAEF,KAAK;AAAA,IAAMG,YAAY;AAAA,EAAA;AACzC,CAAC;AAEI,MAAMC,uBAA4C;AAAA,EACvDC,IAAI;AAAA,EACJC,UAAUxB;AAAAA,EACVyB,OAAOA,CAAC;AAAA,IAAEV;AAAAA,EAAAA,MAAYD,aAAaC,KAAK;AAAA,EACxCW,SAASA,CAACC,SAA6C;AACrD,QAAIA,KAAKC,SAAS5B,eAAe;AAC/B,YAAM,IAAI6B,MAAM,eAAe;AAAA,IACjC;AAEA,UAAMC,UAAUH,KAAKX,SAAS,MAAM;AACpC,QAAI,CAACP,iBAAiBqB,OAAO,GAAG;AAC9B,YAAM,IAAID,MAAM,aAAa;AAAA,IAC/B;AAEA,UAAME,YAAYd,YAAYa,OAAO;AACrC,QAAI,CAACC,UAAUC,KAAAA,KAAU,CAACvB,iBAAiBsB,SAAS,GAAG;AACrD,YAAM,IAAIF,MAAM,qBAAqB;AAAA,IACvC;AAEA,UAAMI,kBAAkBC,OAAOC,KAAKJ,WAAW,MAAM;AACrD,QAAIE,gBAAgBL,SAAS5B,eAAe;AAC1C,YAAM,IAAI6B,MAAM,eAAe;AAAA,IACjC;AAEA,WAAO;AAAA,MAAEF,MAAMM;AAAAA,MAAiBG,UAAU;AAAA,IAAA;AAAA,EAC5C;AACF;AC3BO,MAAMC,mBAAmBC,OAAOC,OAAO,CAACjB,sBAAsBkB,0BAA0B,CAAiC;AAEzH,MAAMC,6BAA6BA,MACxCJ,iBAAiBK,OAAO,CAACC,KAAKC,cAAcC,KAAKF,IAAIA,KAAKC,UAAUpB,QAAQ,GAAG,CAAC;AAE3E,MAAMsB,yBAAyBA,CAACC,QACrCV,iBAAiBW,OAAQJ,CAAAA,cAAcA,UAAUnB,MAAMsB,GAAG,CAAC;AAEtD,MAAME,wBAAwB,OACnCtB,MACAoB,QACwH;AACxH,MAAIG,cAAcvB;AAClB,MAAIwB,kBAAkBJ,IAAIK;AAC1B,MAAIC,kBAAkBN,IAAIO;AAC1B,QAAMC,UAAoB,CAAA;AAC1B,QAAMC,WAAoC,CAAA;AAE1C,aAAWZ,aAAaP,kBAAkB;AACxC,UAAMoB,eAA2C;AAAA,MAC/CL,UAAUD;AAAAA,MACVG,gBAAgBD;AAAAA,MAChBK,WAAWR,YAAYtB;AAAAA,MACvBb,OAAOmC;AAAAA,IAAAA;AAGT,QAAI,CAACN,UAAUnB,MAAMgC,YAAY,EAAG;AAEpC,QAAIP,YAAYtB,SAASgB,UAAUpB,UAAU;AAC3C,YAAM,IAAIK,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM8B,SAAS,MAAMf,UAAUlB,QAAQwB,aAAaO,YAAY;AAChEP,kBAAcS,OAAOhC;AACrB,QAAI,OAAOgC,OAAOP,aAAa,YAAYO,OAAOP,SAASpB,QAAQ;AACjEmB,wBAAkBQ,OAAOP,SAASpB,KAAAA;AAAAA,IACpC;AACA,QAAI,OAAO2B,OAAOvB,aAAa,YAAYuB,OAAOvB,SAASJ,QAAQ;AACjEqB,wBAAkBM,OAAOvB,SAASJ,KAAAA;AAAAA,IACpC;AACA,QAAI2B,OAAOH,YAAY,OAAOG,OAAOH,aAAa,UAAU;AAC1DlB,aAAOsB,OAAOJ,UAAUG,OAAOH,QAAQ;AAAA,IACzC;AACAD,YAAQM,KAAKjB,UAAUrB,EAAE;AAAA,EAC3B;AAEA,SAAO;AAAA,IACLI,MAAMuB;AAAAA,IACNE,UAAUD;AAAAA,IACVf,UAAUiB;AAAAA,IACVE;AAAAA,IACAC;AAAAA,EAAAA;AAEJ;ACzDA,MAAMM,wBAAwB,OAAOC,WAAiD,IAAIC,QAAQ,CAACC,SAASC,WAAW;AACrHH,SAAOI,KAAK,UAAUF,OAAO;AAC7BF,SAAOI,KAAK,SAASD,MAAM;AAC7B,CAAC;AAED,MAAME,gBAAgB,OAAOL,QAA+BM,UAAiC;AAC3F,QAAMC,KAAKP,OAAOQ,MAAMF,KAAK;AAC7B,MAAIC,GAAI;AACR,QAAM,IAAIN,QAAc,CAACC,SAASC,WAAW;AAC3C,UAAMM,UAAUA,MAAM;AACpBC,cAAAA;AACAR,cAAAA;AAAAA,IACF;AAEA,UAAMS,UAAUA,CAACC,UAAmB;AAClCF,cAAAA;AACAP,aAAOS,KAAK;AAAA,IACd;AAEA,UAAMF,UAAUA,MAAM;AACpBV,aAAOa,IAAI,SAASJ,OAAO;AAC3BT,aAAOa,IAAI,SAASF,OAAO;AAAA,IAC7B;AAEAX,WAAOc,GAAG,SAASL,OAAO;AAC1BT,WAAOc,GAAG,SAASH,OAAO;AAAA,EAC5B,CAAC;AACH;AAEA,MAAMI,oBAAoB,OAAOf,WAAmC;AAClE,MAAI,CAACA,OAAQ;AACb,MAAI,OAAQA,OAA+BgB,UAAU,YAAY;AAC/D,QAAI;AACF,YAAOhB,OAAiDgB,MAAAA;AACxD;AAAA,IACF,QAAQ;AAAA,IACN;AAAA,EAEJ;AACA,MAAI;AACF;AAAEhB,WAAoCiB,UAAAA;AAAAA,EACxC,QAAQ;AAAA,EACN;AAEJ;AAEO,MAAMC,iBAAkG,OAC7GC,UACAnC,QAC6C;AAC7C,QAAMoC,WAAWC,YAAYrC,GAAG;AAChC,MAAI,CAACoC,UAAU;AACbpC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMY,WAAWC,OAAOzC,IAAI0C,IAAIC,QAAQH,YAAY,EAAE,EAAEvD,KAAAA;AACxD,MAAI,CAACuD,UAAU;AACbxC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMgB,UAAUC,oBAAoB7C,KAAKoC,QAAQ;AACjD,QAAMU,WAAWC,YAAY/C,KAAKoC,UAAUQ,OAAO;AAEnD,QAAM,CAACI,eAAeC,WAAW,IAAI,MAAMhC,QAAQiC,IAAI,CACrDC,OAAOC,IAAI,mBAAmBN,QAAQ,GACtCK,OAAOC,IAAI,iBAAiBN,QAAQ,CAAmC,CACxE;AAED,MAAI,CAACF,QAAQS,IAAI,UAAU,iBAAiB,GAAG;AAC7CrD,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAM0B,WAAW,MAAMN,cAAcO,QAAQ;AAAA,IAAEC,MAAM,CAAC;AAAA,MAAEC,KAAKjB;AAAAA,IAAAA,GAAYkB,4BAA4Bd,SAAS,MAAM,CAAC;AAAA,EAAA,CAAG,EAAEe,KAAAA;AAC1H,MAAI,CAACL,UAAU;AACbtD,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,MAAI0B,SAASf,WAAW,UAAUe,SAASM,QAAQ;AACjD,WAAO;AAAA,MAAErC,IAAI;AAAA,MAAMqC,QAAQN,SAASM;AAAAA,IAAAA;AAAAA,EACtC;AAEA,QAAMC,SAAS,MAAMb,cAAcc,iBACjC;AAAA,IAAEN,MAAM,CAAC;AAAA,MAAEC,KAAKjB;AAAAA,IAAAA,GAAY;AAAA,MAAED,QAAQ;AAAA,IAAA,GAAemB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,EAAA,GACnG;AAAA,IAAEmB,MAAM;AAAA,MAAExB,QAAQ;AAAA,IAAA;AAAA,IAAgByB,QAAQ;AAAA,MAAEpC,OAAO;AAAA,IAAA;AAAA,EAAG,GACtD;AAAA,IAAEqC,gBAAgB;AAAA,EAAA,CACpB,EAAEN,KAAAA;AAEF,MAAI,CAACE,QAAQ;AACX7D,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMsC,oBAAoBlB,eAAeC,WAAW;AAEpD,QAAMkB,OAAO,MAAMC,sBAAsBhC,QAAQ;AACjD,QAAMiC,WAAWF,KAAKG;AACtB,MAAI,CAACD,UAAU;AACb,UAAMrB,cAAcuB,UAClB;AAAA,MAAEf,MAAM,CAAC;AAAA,QAAEC,KAAKjB;AAAAA,MAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,IAAA,GAC1E;AAAA,MAAEmB,MAAM;AAAA,QAAExB,QAAQ;AAAA,QAASX,OAAO;AAAA,MAAA;AAAA,IAA4B,CAChE;AACA5B,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AACA,QAAM4C,aAAaC,cAAAA;AACnB,QAAMC,SAAS,IAAIC,aAAaN,UAAU;AAAA,IAAEG;AAAAA,EAAAA,CAAY;AAExD,QAAMI,eAAe,OAAOf,OAAOgB,WAAW,WAAWhB,OAAOgB,SAASC;AACzE,QAAMC,oBAAoBrF,2BAAAA;AAC1B,QAAMsF,4BAA4BnB,OAAOlD,aAAaoE;AACtD,QAAME,mBAAmBpB,OAAOxE,SAASJ,KAAAA,EAAOiG,YAAAA;AAChD,QAAMC,cAAcF,qBAAqB,mBAAmBpB,OAAOxD,SAASpB,OAAOiG,YAAAA,EAAcE,SAAS,MAAM;AAEhH,MAAIC,eAA6C;AACjD,MAAIC,gBAAgBzB,OAAOxD;AAC3B,MAAIkF,gBAAgB1B,OAAOxE;AAC3B,MAAImG,YAAY3B,OAAOlD;AACvB,MAAI8E,mBAA6B,CAAA;AACjC,MAAIC,gBAAyC;AAAA,IAC3ClD;AAAAA,IACAJ;AAAAA,IACA/C,UAAUwE,OAAOxE;AAAAA,IACjBsB,WAAWkD,OAAOlD;AAAAA,IAClB,GAAI,OAAOkD,OAAO8B,aAAa,YAAY;AAAA,MAAEA,UAAU9B,OAAO8B;AAAAA,IAAAA,IAAa,CAAA;AAAA,IAC3E,GAAI,OAAO9B,OAAO+B,iBAAiB,WAAW;AAAA,MAAEA,cAAc/B,OAAO+B;AAAAA,IAAAA,IAAiB,CAAA;AAAA,IACtF,GAAIhB,eAAe;AAAA,MAAEC,QAAQD;AAAAA,IAAAA,IAAiB,CAAA;AAAA,EAAC;AAGjD,MAAI;AACF,QAAI,CAACI,6BAA6BG,aAAa;AAC7C,YAAM,IAAIrG,MAAM,eAAe;AAAA,IACjC;AAEA,UAAM+G,SAAS5C,YAAY6C,KAAK;AAAA,MAAEtD;AAAAA,IAAAA,CAAU,EAAEuD,KAAK;AAAA,MAAEC,OAAO;AAAA,IAAA,CAAG,EAAEH,OAAAA;AAIjE,QAAII,gBAAgB;AACpB,UAAMC,SAAmB,CAAA;AACzB,QAAIC,gBAAgB;AAEpB,UAAMC,gBAA0B,CAAA;AAChC,UAAMC,aAAuB,CAAA;AAC7B,QAAIC,aAAa;AAEjB,QAAI;AACF,uBAAiBC,YAAYV,QAAQ;AACnC,YAAIU,SAASP,UAAUC,eAAe;AACpC,gBAAM,IAAInH,MAAM,gBAAgB;AAAA,QAClC;AAEA,cAAMwC,QAAQiF,SAAS3H;AAEvB,YAAIoG,2BAA2B;AAC7BkB,iBAAOpF,KAAKQ,KAAK;AACjB6E,2BAAiB7E,MAAMzC;AAAAA,QACzB,WAAW,CAACwG,cAAc;AACxBe,wBAActF,KAAKQ,KAAK;AAExB,cAAIgF,aAAavB,mBAAmB;AAClC,kBAAMyB,QAAQlF,MAAMmF,SAAS,GAAG3G,KAAK4G,IAAIpF,MAAMzC,QAAQkG,oBAAoBuB,UAAU,CAAC;AACtF,gBAAIE,MAAM3H,QAAQ;AAChBwH,yBAAWvF,KAAK0F,KAAK;AACrBF,4BAAcE,MAAM3H;AAAAA,YACtB;AAAA,UACF;AAEA,cAAIyH,cAAcvB,mBAAmB;AACnC,kBAAM/G,QAAQmB,OAAOwH,OAAON,YAAYC,UAAU;AAClD,kBAAMM,aAAa7G,uBAAuB;AAAA,cACxCM,UAAUwD,OAAOxD;AAAAA,cACjBE,gBAAgBsD,OAAOxE;AAAAA,cACvBsB,WAAWkD,OAAOlD;AAAAA,cAClB3C;AAAAA,YAAAA,CACD;AAED,gBAAI4I,WAAW/H,QAAQ;AACrB,oBAAM,IAAIC,MAAM8H,WAAWC,KAAMhH,CAAAA,cAAcA,UAAUrB,OAAO,cAAc,IAAI,kBAAkB,2BAA2B;AAAA,YACjI;AAEAkH,4BAAgB;AAAA,cACdlD;AAAAA,cACAJ;AAAAA,cACA/C,UAAUwE,OAAOxE;AAAAA,cACjBsB,WAAWkD,OAAOlD;AAAAA,cAClB,GAAI,OAAOkD,OAAO8B,aAAa,YAAY;AAAA,gBAAEA,UAAU9B,OAAO8B;AAAAA,cAAAA,IAAa,CAAA;AAAA,cAC3E,GAAI,OAAO9B,OAAO+B,iBAAiB,WAAW;AAAA,gBAAEA,cAAc/B,OAAO+B;AAAAA,cAAAA,IAAiB,CAAA;AAAA,cACtF,GAAIhB,eAAe;AAAA,gBAAEC,QAAQD;AAAAA,cAAAA,IAAiB,CAAA;AAAA,YAAC;AAEjDS,2BAAeX,OAAOoC,iBAAiBjD,OAAOxD,UAAU;AAAA,cACtDI,UAAUiF;AAAAA,YAAAA,CACX;AAED,uBAAWqB,WAAWX,eAAe;AACnC,oBAAM/E,cAAcgE,cAAc0B,OAAO;AAAA,YAC3C;AACAX,0BAAcvH,SAAS;AAAA,UACzB;AAAA,QACF,OAAO;AACL,gBAAMwC,cAAcgE,cAAc/D,KAAK;AAAA,QACzC;AAEA2E,yBAAiB;AAAA,MACnB;AAAA,IACF,UAAA;AACE,UAAI;AACF,cAAMJ,OAAOmB,MAAAA;AAAAA,MACf,QAAQ;AAAA,MACN;AAAA,IAEJ;AAEA,QAAIf,kBAAkBpC,OAAOoD,aAAa;AACxC,YAAM,IAAInI,MAAM,gBAAgB;AAAA,IAClC;AAEA,QAAIkG,2BAA2B;AAC7B,YAAMkC,YAAY/H,OAAOwH,OAAOT,QAAQC,aAAa;AACrD,YAAM;AAAA,QACJvH,MAAMuI;AAAAA,QACN9G,UAAU+G;AAAAA,QACV/H,UAAUgI;AAAAA,QACV7G;AAAAA,QACAC,UAAU6G;AAAAA,MAAAA,IACR,MAAMpH,sBAAsBgH,WAAW;AAAA,QACzC7G,UAAUwD,OAAOxD;AAAAA,QACjBE,gBAAgBsD,OAAOxE;AAAAA,MAAAA,CACxB;AAEDiG,sBAAgB8B;AAChB7B,sBAAgB8B;AAChB7B,kBAAY2B,UAAUtI;AACtB4G,yBAAmBjF;AACnBkF,sBAAgB;AAAA,QACdlD;AAAAA,QACAJ;AAAAA,QACA/C,UAAUgI;AAAAA,QACV1G,WAAWwG,UAAUtI;AAAAA,QACrB,GAAI2B,QAAQ3B,SAAS;AAAA,UAAE+H,YAAYpG;AAAAA,UAAS+G,iBAAiB1D,OAAOlD;AAAAA,QAAAA,IAAc,CAAA;AAAA,QAClF,GAAG2G;AAAAA,QACH,GAAI,OAAOzD,OAAO8B,aAAa,YAAY;AAAA,UAAEA,UAAU9B,OAAO8B;AAAAA,QAAAA,IAAa,CAAA;AAAA,QAC3E,GAAI,OAAO9B,OAAO+B,iBAAiB,WAAW;AAAA,UAAEA,cAAc/B,OAAO+B;AAAAA,QAAAA,IAAiB,CAAA;AAAA,QACtF,GAAIhB,eAAe;AAAA,UAAEC,QAAQD;AAAAA,QAAAA,IAAiB,CAAA;AAAA,MAAC;AAEjDS,qBAAeX,OAAOoC,iBAAiBxB,eAAe;AAAA,QACpD7E,UAAUiF;AAAAA,MAAAA,CACX;AAED,YAAM8B,WAAWzG,sBAAsBsE,YAAY;AACnDA,mBAAaoC,IAAIN,SAAS;AAC1B,YAAMK;AAAAA,IACR,OAAO;AACL,UAAI,CAACnC,cAAc;AACjB,cAAMrH,QAAQmB,OAAOwH,OAAON,YAAYC,UAAU;AAClD,cAAMM,aAAa7G,uBAAuB;AAAA,UACxCM,UAAUwD,OAAOxD;AAAAA,UACjBE,gBAAgBsD,OAAOxE;AAAAA,UACvBsB,WAAWkD,OAAOlD;AAAAA,UAClB3C;AAAAA,QAAAA,CACD;AAED,YAAI4I,WAAW/H,QAAQ;AACrB,gBAAM,IAAIC,MAAM8H,WAAWC,KAAMhH,CAAAA,cAAcA,UAAUrB,OAAO,cAAc,IAAI,kBAAkB,2BAA2B;AAAA,QACjI;AAEAkH,wBAAgB;AAAA,UACdlD;AAAAA,UACAJ;AAAAA,UACA/C,UAAUwE,OAAOxE;AAAAA,UACjBsB,WAAWkD,OAAOlD;AAAAA,UAClB,GAAI,OAAOkD,OAAO8B,aAAa,YAAY;AAAA,YAAEA,UAAU9B,OAAO8B;AAAAA,UAAAA,IAAa,CAAA;AAAA,UAC3E,GAAI,OAAO9B,OAAO+B,iBAAiB,WAAW;AAAA,YAAEA,cAAc/B,OAAO+B;AAAAA,UAAAA,IAAiB,CAAA;AAAA,UACtF,GAAIhB,eAAe;AAAA,YAAEC,QAAQD;AAAAA,UAAAA,IAAiB,CAAA;AAAA,QAAC;AAEjDS,uBAAeX,OAAOoC,iBAAiBjD,OAAOxD,UAAU;AAAA,UACtDI,UAAUiF;AAAAA,QAAAA,CACX;AAED,mBAAWqB,WAAWX,eAAe;AACnC,gBAAM/E,cAAcgE,cAAc0B,OAAO;AAAA,QAC3C;AACAX,sBAAcvH,SAAS;AAAA,MACzB;AAEA,YAAM2I,WAAWzG,sBAAsBsE,YAAY;AACnDA,mBAAaoC,IAAAA;AACb,YAAMD;AAAAA,IACR;AAEA,UAAM5D,SAASnB,OAAQ4C,aAA6C7G,MAAM,EAAE;AAC5E,QAAI,CAACoF,QAAQ;AACX,YAAM,IAAI9E,MAAM,iBAAiB;AAAA,IACnC;AAEA,UAAMkE,cAAcuB,UAClB;AAAA,MAAEf,MAAM,CAAC;AAAA,QAAEC,KAAKjB;AAAAA,MAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,IAAA,GAC1E;AAAA,MAAEmB,MAAM;AAAA,QAAExB,QAAQ;AAAA,QAAQqB;AAAAA,QAAQvD,UAAUiF;AAAAA,QAAejG,UAAUkG;AAAAA,QAAe5E,WAAW6E;AAAAA,MAAAA;AAAAA,MAAaxB,QAAQ;AAAA,QAAEpC,OAAO;AAAA,MAAA;AAAA,IAAG,CAClI;AAEA,UAAM8F,4BAA4B;AAAA,MAChCtF;AAAAA,MACAI;AAAAA,MACAoB;AAAAA,MACAvD,UAAUiF;AAAAA,MACVjG,UAAUkG;AAAAA,MACVhF,gBAAgBsD,OAAOxE;AAAAA,MACvBsB,WAAW6E;AAAAA,MACX,GAAI,OAAO3B,OAAO8B,aAAa,YAAY;AAAA,QAAEA,UAAU9B,OAAO8B;AAAAA,MAAAA,IAAa,CAAA;AAAA,MAC3E,GAAI,OAAO9B,OAAO+B,iBAAiB,WAAW;AAAA,QAAEA,cAAc/B,OAAO+B;AAAAA,MAAAA,IAAiB,CAAA;AAAA,MACtF,GAAIhB,eAAe;AAAA,QAAEC,QAAQD;AAAAA,MAAAA,IAAiB,CAAA;AAAA,MAC9Ca;AAAAA,MACAhF,UAAUiF;AAAAA,IAAAA,CACX,EAAEiC,MAAO/F,CAAAA,UAAU;AAClBgG,cAAQhG,MAAM,wCAAwC;AAAA,QACpDQ;AAAAA,QACAI;AAAAA,QACAoB;AAAAA,QACAhC;AAAAA,MAAAA,CACD;AAAA,IACH,CAAC;AAED,QAAI;AACF,YAAMqB,YAAY4E,WAAW;AAAA,QAAErF;AAAAA,MAAAA,CAAU;AAAA,IAC3C,QAAQ;AAAA,IACN;AAGF,WAAO;AAAA,MAAEjB,IAAI;AAAA,MAAMqC;AAAAA,IAAAA;AAAAA,EACrB,SAAShC,OAAO;AACd,UAAMkG,UAAUlG,iBAAiB9C,QAAQ8C,MAAMkG,UAAUrF,OAAOb,KAAK;AAErE,UAAMG,kBAAkBsD,YAAY;AAEpC,QAAIyC,YAAY,kBAAkB;AAChC,YAAM9E,cAAcuB,UAClB;AAAA,QAAEf,MAAM,CAAC;AAAA,UAAEC,KAAKjB;AAAAA,QAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,MAAA,GAC1E;AAAA,QAAEmB,MAAM;AAAA,UAAExB,QAAQ;AAAA,QAAA;AAAA,MAAY,CAChC;AACAvC,UAAIsC,IAAIC,OAAO,GAAG;AAClB,aAAO;AAAA,QAAEhB,IAAI;AAAA,QAAOK,OAAO;AAAA,MAAA;AAAA,IAC7B;AAEA,QAAIkG,YAAY,iBAAiB;AAC/B,YAAM9E,cAAcuB,UAClB;AAAA,QAAEf,MAAM,CAAC;AAAA,UAAEC,KAAKjB;AAAAA,QAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,MAAA,GAC1E;AAAA,QAAEmB,MAAM;AAAA,UAAExB,QAAQ;AAAA,UAASX,OAAOkG;AAAAA,QAAAA;AAAAA,MAAQ,CAC5C;AACA9H,UAAIsC,IAAIC,OAAO,GAAG;AAClB,aAAO;AAAA,QAAEhB,IAAI;AAAA,QAAOK,OAAOkG;AAAAA,MAAAA;AAAAA,IAC7B;AAEA,QAAIA,YAAY,oBAAoBA,YAAY,6BAA6B;AAC3E,YAAM9E,cAAcuB,UAClB;AAAA,QAAEf,MAAM,CAAC;AAAA,UAAEC,KAAKjB;AAAAA,QAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,MAAA,GAC1E;AAAA,QAAEmB,MAAM;AAAA,UAAExB,QAAQ;AAAA,UAASX,OAAOkG;AAAAA,QAAAA;AAAAA,MAAQ,CAC5C;AACA9H,UAAIsC,IAAIC,OAAO,GAAG;AAClB,aAAO;AAAA,QAAEhB,IAAI;AAAA,QAAOK,OAAOkG;AAAAA,MAAAA;AAAAA,IAC7B;AAEA,QAAIA,QAAQjK,WAAW,yBAAyB,GAAG;AACjD,YAAMmF,cAAcuB,UAClB;AAAA,QAAEf,MAAM,CAAC;AAAA,UAAEC,KAAKjB;AAAAA,QAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,MAAA,GAC1E;AAAA,QAAEmB,MAAM;AAAA,UAAExB,QAAQ;AAAA,UAASX,OAAOkG;AAAAA,QAAAA;AAAAA,MAAQ,CAC5C;AACA9H,UAAIsC,IAAIC,OAAO,GAAG;AAClB,aAAO;AAAA,QAAEhB,IAAI;AAAA,QAAOK,OAAOkG;AAAAA,MAAAA;AAAAA,IAC7B;AAEA,QAAIA,YAAY,iBAAiBA,YAAY,uBAAuB;AAClE,YAAM9E,cAAcuB,UAClB;AAAA,QAAEf,MAAM,CAAC;AAAA,UAAEC,KAAKjB;AAAAA,QAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,MAAA,GAC1E;AAAA,QAAEmB,MAAM;AAAA,UAAExB,QAAQ;AAAA,UAASX,OAAOkG;AAAAA,QAAAA;AAAAA,MAAQ,CAC5C;AACA9H,UAAIsC,IAAIC,OAAO,GAAG;AAClB,aAAO;AAAA,QAAEhB,IAAI;AAAA,QAAOK,OAAOkG;AAAAA,MAAAA;AAAAA,IAC7B;AAEA,UAAM9E,cAAcuB,UAClB;AAAA,MAAEf,MAAM,CAAC;AAAA,QAAEC,KAAKjB;AAAAA,MAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,IAAA,GAC1E;AAAA,MAAEmB,MAAM;AAAA,QAAExB,QAAQ;AAAA,QAASX,OAAOkG;AAAAA,MAAAA;AAAAA,IAAQ,CAC5C;AAEA9H,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AACF;AC3YO,MAAMmG,YAA2F,OACtG5F,UACAnC,QAC2C;AAC3C,QAAMoC,WAAWC,YAAYrC,GAAG;AAChC,MAAI,CAACoC,UAAU;AACbpC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMY,WAAWC,OAAOzC,IAAI0C,IAAIC,QAAQH,YAAY,EAAE,EAAEvD,KAAAA;AACxD,MAAI,CAACuD,UAAU;AACbxC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMgB,UAAUC,oBAAoB7C,KAAKoC,QAAQ;AACjD,QAAMU,WAAWC,YAAY/C,KAAKoC,UAAUQ,OAAO;AAEnD,QAAM,CAACI,eAAeC,WAAW,IAAI,MAAMhC,QAAQiC,IAAI,CACrDC,OAAOC,IAAI,mBAAmBN,QAAQ,GACtCK,OAAOC,IAAI,iBAAiBN,QAAQ,CAAmC,CACxE;AAED,MAAI,CAACF,QAAQS,IAAI,QAAQ,iBAAiB,GAAG;AAC3CrD,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMoG,UAAU,MAAMhF,cAAcO,QAAQ;AAAA,IAAEC,MAAM,CAAC;AAAA,MAAEC,KAAKjB;AAAAA,IAAAA,GAAYkB,4BAA4Bd,SAAS,MAAM,CAAC;AAAA,EAAA,CAAG,EAAEe,KAAAA;AACzH,MAAI,CAACqE,SAAS;AACZhI,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMqG,eAAe,MAAMhF,YAAY6C,KACrC;AAAA,IAAEtD;AAAAA,EAAAA,GACF;AAAA,IAAEwD,OAAO;AAAA,IAAGvC,KAAK;AAAA,EAAA,CACnB,EAAEsC,KAAK;AAAA,IAAEC,OAAO;AAAA,EAAA,CAAG,EAAErC,KAAAA;AAErB,QAAMuE,WAAYD,aACfE,IAAKC,SAAS,OAAOA,IAAIpC,UAAU,WAAWoC,IAAIpC,QAAQ,EAAG,EAC7D/F,OAAQoI,CAAAA,MAAMC,OAAOC,UAAUF,CAAC,KAAKA,KAAK,CAAC;AAE9C,SAAO;AAAA,IACL9G,IAAI;AAAA,IACJgB,QAAQyF,QAAQzF;AAAAA,IAChBiG,WAAWR,QAAQQ;AAAAA,IACnBvB,aAAae,QAAQf;AAAAA,IACrBiB;AAAAA,IACA,GAAIF,QAAQpE,SAAS;AAAA,MAAEA,QAAQoE,QAAQpE;AAAAA,IAAAA,IAAW,CAAA;AAAA,EAAC;AAEvD;ACjEO,MAAM6E,YAAY;AAClB,MAAMC,aAAa;AACnB,MAAMC,cAAc;AACpB,MAAMC,gBAAgB;AAEtB,MAAMC,oBAAoBC,OAAS;AAAA,EACxCzI,UAAUyI,OAAEC,EAASrC,IAAI,CAAC;AAAA,EAC1BrH,UAAUyJ,OAAEC,EAASrC,IAAI,CAAC;AAAA,EAC1Bf,UAAUmD,QAAEE,EAAUC,SAAAA;AAAAA,EACtBtI,WAAWmI,OAAEI,EAASC,IAAAA,EAAMzC,IAAI,CAAC;AACnC,CAAC;AAIiCoC,OAAS;AAAA,EACzCvH,IAAIuH,QAAEE;AAAAA,EACNpH,OAAOkH,OAAEC,EAASE,SAAAA;AAAAA,EAClBzG,UAAUsG,OAAEC,EAASE,SAAAA;AAAAA,EACrBG,WAAWN,OAAEC,EAASE,SAAAA;AAAAA,EACtBT,WAAWM,OAAEI,EAASC,IAAAA,EAAMF,SAAAA;AAAAA,EAC5BhC,aAAa6B,OAAEI,EAASC,IAAAA,EAAMF,SAAAA;AAChC,CAAC;AAImCH,OAAS;AAAA,EAC3CvH,IAAIuH,QAAEE;AAAAA,EACNpH,OAAOkH,OAAEC,EAASE,SAAAA;AAAAA,EAClB1G,QAAQuG,MAAO,CAAC,aAAa,cAAc,QAAQ,OAAO,CAAC,EAAEG,SAAAA;AAAAA,EAC7DT,WAAWM,OAAEI,EAASC,IAAAA,EAAMF,SAAAA;AAAAA,EAC5BhC,aAAa6B,OAAEI,EAASC,IAAAA,EAAMF,SAAAA;AAAAA,EAC9Bf,UAAUY,MAAQA,SAAWK,IAAAA,EAAMzC,IAAI,CAAC,CAAC,EAAEuC,SAAAA;AAAAA,EAC3CrF,QAAQkF,OAAEC,EAASE,SAAAA;AACrB,CAAC;AAIqCH,OAAS;AAAA,EAC7CvH,IAAIuH,QAAEE;AAAAA,EACNpH,OAAOkH,OAAEC,EAASE,SAAAA;AAAAA,EAClBrF,QAAQkF,OAAEC,EAASE,SAAAA;AACrB,CAAC;ACrBM,MAAMI,aAA+F,OAC1GC,SACAtJ,QACyC;AACzC,QAAMoC,WAAWC,YAAYrC,GAAG;AAChC,MAAI,CAACoC,UAAU;AACbpC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMiD,SAAS0E,UAAUvJ,GAAG;AAE5B,QAAMwJ,SAASC,kBAA0BC,UAAUJ,WAAW,CAAA,CAAE;AAChE,MAAI,CAACE,OAAOG,SAAS;AACnB3J,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAM4G,YAAYoB,kBAAAA;AAClB,QAAM;AAAA,IAAEvJ;AAAAA,IAAUhB;AAAAA,IAAUsB;AAAAA,IAAWgF;AAAAA,EAAAA,IAAa6D,OAAO5K;AAC3D,QAAMqI,cAAcnH,KAAK+J,KAAKlJ,YAAY6H,SAAS;AAEnD,QAAM5F,UAAUC,oBAAoB7C,KAAKoC,QAAQ;AACjD,QAAMU,WAAWC,YAAY/C,KAAKoC,UAAUQ,OAAO;AAEnD,QAAM,CAACI,eAAeC,WAAW,IAAI,MAAMhC,QAAQiC,IAAI,CACrDC,OAAOC,IAAI,mBAAmBN,QAAQ,GACtCK,OAAOC,IAAI,iBAAiBN,QAAQ,CAAmC,CACxE;AAED,QAAMoB,oBAAoBlB,eAAeC,WAAW;AAEpD,QAAMT,WAAW,IAAIsH,SAAAA,EAAW7L,SAAAA;AAChC,QAAM8L,MAAMC,KAAKD,IAAAA;AACjB,QAAME,YAAY,IAAID,KAAKD,MAAMG,iBAAiB;AAElD,QAAMd,YAAYvE,SAAS,OAAOsF,YAAY,EAAE,EAAElM,SAAS,WAAW;AACtE,QAAM2H,eAAewD,YAAYgB,iBAAiBjL,OAAOC,KAAKgK,SAAS,CAAC,IAAItE;AAE5E,QAAM9B,cAAcqH,OAAO;AAAA,IACzB5G,KAAKjB;AAAAA,IACL,GAAIqC,SAAS;AAAA,MAAEA;AAAAA,IAAAA,IAAW,CAAA;AAAA,IAC1B,GAAIe,eAAe;AAAA,MAAEA;AAAAA,IAAAA,IAAiB,CAAA;AAAA,IACtCvF;AAAAA,IACAhB;AAAAA,IACA,GAAI,OAAOsG,aAAa,YAAY;AAAA,MAAEA;AAAAA,IAAAA,IAAa,CAAA;AAAA,IACnDhF;AAAAA,IACA6H;AAAAA,IACAvB;AAAAA,IACA1E,QAAQ;AAAA,IACR+H,WAAW,IAAIN,KAAKD,GAAG;AAAA,IACvBE;AAAAA,EAAAA,CACD;AAED,SAAO;AAAA,IACL1I,IAAI;AAAA,IACJiB;AAAAA,IACAgG;AAAAA,IACAvB;AAAAA,IACA,GAAImC,YAAY;AAAA,MAAEA;AAAAA,IAAAA,IAAc,CAAA;AAAA,EAAC;AAErC;AC5DO,MAAMmB,cAAqE,OAChFjB,SACAtJ,QACkC;AAClC,QAAMoC,WAAWC,YAAYrC,GAAG;AAChC,MAAI,CAACoC,UAAU;AACbpC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMY,WAAWC,OAAOzC,IAAI0C,IAAIC,QAAQH,YAAY,EAAE,EAAEvD,KAAAA;AACxD,QAAMuL,WAAW/H,OAAOzC,IAAI0C,IAAIC,QAAQqD,SAAS,EAAE,EAAE/G,KAAAA;AACrD,QAAM+G,QAAQsC,OAAOkC,QAAQ;AAE7B,MAAI,CAAChI,YAAY,CAAC8F,OAAOC,UAAUvC,KAAK,KAAKA,QAAQ,GAAG;AACtDhG,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMgB,UAAUC,oBAAoB7C,KAAKoC,QAAQ;AACjD,QAAMU,WAAWC,YAAY/C,KAAKoC,UAAUQ,OAAO;AAEnD,QAAM,CAACI,eAAeC,WAAW,IAAI,MAAMhC,QAAQiC,IAAI,CACrDC,OAAOC,IAAI,mBAAmBN,QAAQ,GACtCK,OAAOC,IAAI,iBAAiBN,QAAQ,CAAmC,CACxE;AAED,MAAI,CAACF,QAAQS,IAAI,UAAU,iBAAiB,GAAG;AAC7CrD,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMoG,UAAU,MAAMhF,cAAcO,QAAQ;AAAA,IAAEC,MAAM,CAAC;AAAA,MAAEC,KAAKjB;AAAAA,IAAAA,GAAYkB,4BAA4Bd,SAAS,QAAQ,CAAC;AAAA,EAAA,CAAG,EAAEe,KAAAA;AAC3H,MAAI,CAACqE,SAAS;AACZhI,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,MAAIoG,QAAQzF,WAAW,aAAa;AAClCvC,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,MAAIoE,SAASgC,QAAQf,aAAa;AAChCjH,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAMhD,OAAO6L,gBAAgBnB,OAAO;AACpC,MAAI,CAAC1K,MAAM;AACToB,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAM8I,eAAe1E,UAAUgC,QAAQf,cAAc,IACjDe,QAAQrH,YAAYqH,QAAQQ,aAAaR,QAAQf,cAAc,KAC/De,QAAQQ;AAEZ,MAAI5J,KAAKC,SAAS6L,cAAc;AAC9B1K,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,MAAIhD,KAAKC,WAAW6L,cAAc;AAChC1K,QAAIsC,IAAIC,OAAO,GAAG;AAClB,WAAO;AAAA,MAAEhB,IAAI;AAAA,MAAOK,OAAO;AAAA,IAAA;AAAA,EAC7B;AAEA,QAAM+I,iBAAiB3K,IAAI0C,IAAIU,IAAI,gBAAgB;AACnD,QAAMwH,SAASD,iBAAiBP,iBAAiBxL,IAAI,IAAIkG;AAEzD,MAAI6F,gBAAgB;AAClB,UAAME,iBAAiBC,mBAAmBH,cAAc;AACxD,QAAIC,WAAWC,gBAAgB;AAC7B7K,UAAIsC,IAAIC,OAAO,GAAG;AAClB,aAAO;AAAA,QAAEhB,IAAI;AAAA,QAAOK,OAAO;AAAA,MAAA;AAAA,IAC7B;AAAA,EACF;AAEA,QAAMsC,oBAAoBlB,eAAeC,WAAW;AAEpD,QAAMA,YAAYsB,UAChB;AAAA,IAAE/B;AAAAA,IAAUwD;AAAAA,EAAAA,GACZ;AAAA,IACEjC,MAAM;AAAA,MACJvB;AAAAA,MACAwD;AAAAA,MACApH;AAAAA,MACAmM,MAAMnM,KAAKC;AAAAA,MACX+L;AAAAA,MACAX,WAAWjC,QAAQiC;AAAAA,IAAAA;AAAAA,IAErBe,cAAc;AAAA,MACZV,+BAAeN,KAAAA;AAAAA,IAAK;AAAA,EACtB,GAEF;AAAA,IAAEiB,QAAQ;AAAA,EAAA,CACZ;AAEAjL,MAAIsC,IAAIC,OAAO,GAAG;AAClB,SAAO;AAAA,IAAEhB,IAAI;AAAA,EAAA;AACf;AC7HO,MAAM2J,gBAAgBA,CAAC;AAAA,EAC5BC;AAAAA,EACAC;AAIF,MAAM;AACJ,SAAO,CAAC1I,KAAUJ,KAAU+I,SAAc;AACxC,UAAMC,cAAc,OAAO5I,KAAK6I,UAAU,cAAc,MAAM,WAC1D9I,OAAOC,IAAI6I,QAAQ,cAAc,CAAC,IAClC;AAEJ,QAAI,CAACD,YAAYE,SAAS,0BAA0B,GAAG;AACrDH,WAAAA;AACA;AAAA,IACF;AAEA,QAAII,QAAQ;AACZ,UAAMvF,SAAmB,CAAA;AACzB,QAAIwF,OAAO;AACX,QAAIC,SAAS;AACb,QAAIC,kBAAwD;AAE5D,UAAMC,qBAAqB,OAAOT,4BAA4B,YAAYA,0BAA0B,IAChGA,0BACA;AAEJ,UAAM1J,UAAUA,MAAM;AACpBgB,UAAIb,IAAI,QAAQiK,MAAM;AACtBpJ,UAAIb,IAAI,OAAOkK,KAAK;AACpBrJ,UAAIb,IAAI,SAASF,OAAO;AACxBe,UAAIb,IAAI,WAAWmK,SAAS;AAC5B,UAAIJ,iBAAiB;AACnBK,qBAAaL,eAAe;AAC5BA,0BAAkB;AAAA,MACpB;AAAA,IACF;AAEA,UAAMM,SAASA,CAACtK,UAAoB;AAClC,UAAI8J,KAAM;AACVA,aAAO;AAEPhK,cAAAA;AAEA,UAAIE,OAAO;AACTyJ,aAAKzJ,KAAK;AACV;AAAA,MACF;AAEAc,UAAIyJ,OAAOhN,OAAOwH,OAAOT,QAAQuF,KAAK;AACtCJ,WAAAA;AAAAA,IACF;AAEA,UAAMS,SAASA,CAACxK,UAAe;AAC7B,UAAIoK,KAAM;AACV,YAAMU,SAASjN,OAAOkN,SAAS/K,KAAK,IAAIA,QAAQnC,OAAOC,KAAKkC,KAAK;AACjEmK,eAASW,OAAOvN;AAEhB,UAAI4M,QAAQN,YAAY;AACtBO,eAAO;AACPhK,gBAAAA;AACAgB,YAAIT,QAAAA;AACJK,YAAIC,OAAO,GAAG,EAAE+J,KAAK;AAAA,UAAE/K,IAAI;AAAA,UAAOK,OAAO;AAAA,QAAA,CAAmB;AAC5D;AAAA,MACF;AAEAsE,aAAOpF,KAAKsL,MAAM;AAElB,UAAI,CAACP,mBAAoB;AAEzB,YAAM9B,MAAMC,KAAKD,IAAAA;AACjB,YAAMwC,YAAYC,aAAa9J,GAAG;AAClC,YAAM+J,QAAQC,mBAAmBH,WAAWV,oBAAoB9B,GAAG;AACnE,YAAM4C,SAASC,kBAAkBH,OAAOL,OAAOvN,QAAQgN,oBAAoB9B,GAAG;AAE9E,UAAI4C,SAAS,KAAK,CAAChB,QAAQ;AACzBA,iBAAS;AACTjJ,YAAImK,MAAAA;AACJjB,0BAAkBkB,WAAW,MAAM;AACjClB,4BAAkB;AAClBD,mBAAS;AACT,cAAID,KAAM;AACV,cAAI;AACFhJ,gBAAIqK,OAAAA;AAAAA,UACN,QAAQ;AAAA,UACN;AAAA,QAEJ,GAAGJ,MAAM;AAAA,MACX;AAAA,IACF;AAEA,UAAMZ,QAAQA,MAAMG,OAAAA;AACpB,UAAMvK,UAAUA,CAACqL,QAAiBd,OAAOc,GAAG;AAC5C,UAAMhB,YAAYA,MAAME,OAAO,IAAIpN,MAAM,iBAAiB,CAAC;AAE3D4D,QAAIZ,GAAG,QAAQgK,MAAM;AACrBpJ,QAAIZ,GAAG,OAAOiK,KAAK;AACnBrJ,QAAIZ,GAAG,SAASH,OAAO;AACvBe,QAAIZ,GAAG,WAAWkK,SAAS;AAAA,EAC7B;AACF;AAQA,MAAMiB,oBAAoB;AAC1B,MAAMC,kBAAkB,KAAK,KAAK;AAElC,MAAMC,uCAAuBC,IAAAA;AAC7B,IAAIC,gBAAgB;AAEpB,MAAMb,eAAeA,CAAC9J,QAAqB;AACzC,QAAM4K,cAAc,OAAO5K,KAAK6K,aAAa,WAAW7K,IAAI6K,WAAW;AACvE,MAAID,YAAYrO,KAAAA,EAAQ,QAAOqO,YAAYrO,KAAAA;AAC3C,QAAMuO,QAAQ,OAAO9K,KAAK+K,OAAO,WAAW/K,IAAI+K,KAAK;AACrD,SAAOD,MAAMvO,UAAU;AACzB;AAEA,MAAMyO,qBAAqBA,CAAC3D,QAAgB;AAC1C,MAAIA,MAAMsD,gBAAgB,IAAQ;AAClCA,kBAAgBtD;AAEhB,MAAIoD,iBAAiBpC,OAAO,IAAM;AAElC,aAAW,CAAC4C,KAAKlB,KAAK,KAAKU,kBAAkB;AAC3C,QAAIpD,MAAM0C,MAAMmB,aAAaV,iBAAiB;AAC5CC,uBAAiBU,OAAOF,GAAG;AAAA,IAC7B;AAAA,EACF;AACF;AAEA,MAAMjB,qBAAqBA,CAACiB,KAAa9B,oBAA4B9B,QAAiC;AACpG2D,qBAAmB3D,GAAG;AAEtB,QAAM+D,WAAWjC,qBAAqBoB;AACtC,QAAM3J,WAAW6J,iBAAiB/J,IAAIuK,GAAG;AACzC,MAAIrK,UAAU;AACZA,aAASsK,aAAa7D;AACtBzG,aAASyK,SAASjO,KAAK4G,IAAIoH,UAAUxK,SAASyK,MAAM;AACpD,WAAOzK;AAAAA,EACT;AAEA,QAAM+H,OAAwB;AAAA,IAC5B0C,QAAQD;AAAAA,IACRE,cAAcjE;AAAAA,IACd6D,YAAY7D;AAAAA,EAAAA;AAEdoD,mBAAiBc,IAAIN,KAAKtC,IAAI;AAC9B,SAAOA;AACT;AAEA,MAAMuB,oBAAoBA,CACxBH,OACAyB,OACArC,oBACA9B,QACW;AACX,QAAM+D,WAAWjC,qBAAqBoB;AACtC,QAAMkB,YAAYrO,KAAKF,IAAI,GAAGmK,MAAM0C,MAAMuB,YAAY;AAEtD,MAAIG,YAAY,GAAG;AACjB1B,UAAMsB,SAASjO,KAAK4G,IAAIoH,UAAUrB,MAAMsB,SAAUI,YAAYtC,qBAAsB,GAAI;AACxFY,UAAMuB,eAAejE;AAAAA,EACvB;AAEA0C,QAAMsB,UAAUG;AAEhB,MAAIzB,MAAMsB,UAAU,EAAG,QAAO;AAC9B,SAAOjO,KAAK+J,KAAM,CAAC4C,MAAMsB,SAASlC,qBAAsB,GAAI;AAC9D;AChKA,MAAA,UAAe,CAACuC,QAA0B;AACxC,QAAMC,iBAAiBzE,kBAAAA;AACvBwE,MAAIE,IACF7E,WACAyB,cAAc;AAAA,IACZC,YAAYoD,qBAAqBF,cAAc;AAAA,IAC/CjD,yBAAyBoD,iCAAAA;AAAAA,EAAiC,CAC3D,CACH;AAEAJ,MAAIK,KAAKhF,WAAmBJ,UAAU;AACtC+E,MAAIM,IAAIjF,YAAoBc,WAAW;AACvC6D,MAAIhL,IAAIqG,aAAqB1B,SAAS;AACtCqG,MAAIK,KAAKhF,eAAuBvH,cAAc;AAChD;"}
|
|
1
|
+
{"version":3,"file":"handler-D-XdgeG_.js","sources":["../src/uploads/api/file-uploads/processors/sanitizeSvg.ts","../src/uploads/api/file-uploads/processors/index.ts","../src/uploads/api/file-uploads/handlers/completeUpload.ts","../src/uploads/api/file-uploads/handlers/getStatus.ts","../src/uploads/api/file-uploads/index.ts","../src/uploads/api/file-uploads/handlers/initUpload.ts","../src/uploads/api/file-uploads/handlers/uploadChunk.ts","../src/uploads/api/file-uploads/middleware/rawBodyParser.ts","../src/uploads/api/file-uploads/handler.ts"],"sourcesContent":["import { JSDOM } from \"jsdom\"\nimport createDOMPurify from \"dompurify\"\n\nimport type { UploadFileProcessor } from \"./index\"\n\n\nconst MAX_SVG_BYTES = 128 * 1024\n\nconst window = new JSDOM(\"\").window\nconst DOMPurify = createDOMPurify(window)\n\nconst normalizeForSniff = (raw: string): string => raw.replace(/^\\uFEFF/, \"\").trimStart()\n\nconst looksLikeSvgText = (text: string): boolean => {\n const normalized = normalizeForSniff(text)\n if (!normalized.startsWith(\"<\")) return false\n return /<svg(?:\\s|>)/i.test(normalized)\n}\n\nexport const looksLikeSvg = (sniff: Buffer): boolean => looksLikeSvgText(sniff.toString(\"utf8\"))\n\nexport const sanitizeSvg = (svg: string): string =>\n DOMPurify.sanitize(svg, {\n USE_PROFILES: { svg: true, svgFilters: true },\n })\n\nexport const sanitizeSvgProcessor: UploadFileProcessor = {\n id: \"sanitize-svg\",\n maxBytes: MAX_SVG_BYTES,\n match: ({ sniff }) => looksLikeSvg(sniff),\n process: (data): { data: Buffer; mimeType: string } => {\n if (data.length > MAX_SVG_BYTES) {\n throw new Error(\"svg_too_large\")\n }\n\n const svgText = data.toString(\"utf8\")\n if (!looksLikeSvgText(svgText)) {\n throw new Error(\"svg_invalid\")\n }\n\n const sanitized = sanitizeSvg(svgText)\n if (!sanitized.trim() || !looksLikeSvgText(sanitized)) {\n throw new Error(\"svg_sanitize_failed\")\n }\n\n const sanitizedBuffer = Buffer.from(sanitized, \"utf8\")\n if (sanitizedBuffer.length > MAX_SVG_BYTES) {\n throw new Error(\"svg_too_large\")\n }\n\n return { data: sanitizedBuffer, mimeType: \"image/svg+xml\" }\n },\n}\n\n","import { convertHeifToWebpProcessor } from \"./convertHeifToWebp\"\nimport { sanitizeSvgProcessor } from \"./sanitizeSvg\"\n\n\nexport type UploadFileProcessorContext = {\n filename: string\n clientMimeType: string\n totalSize: number\n sniff: Buffer\n}\n\nexport type UploadFileProcessorResult = {\n data: Buffer\n filename?: string\n mimeType?: string\n metadata?: Record<string, unknown>\n}\n\nexport type UploadFileProcessor = {\n id: string\n maxBytes: number\n match: (ctx: UploadFileProcessorContext) => boolean\n process: (data: Buffer, ctx: UploadFileProcessorContext) => Promise<UploadFileProcessorResult> | UploadFileProcessorResult\n}\n\nexport const uploadProcessors = Object.freeze([sanitizeSvgProcessor, convertHeifToWebpProcessor] satisfies UploadFileProcessor[])\n\nexport const getMaxUploadProcessorBytes = (): number =>\n uploadProcessors.reduce((max, processor) => Math.max(max, processor.maxBytes), 0)\n\nexport const selectUploadProcessors = (ctx: UploadFileProcessorContext): UploadFileProcessor[] =>\n uploadProcessors.filter((processor) => processor.match(ctx))\n\nexport const applyUploadProcessors = async (\n data: Buffer,\n ctx: Omit<UploadFileProcessorContext, \"sniff\" | \"totalSize\">,\n): Promise<{ data: Buffer; filename: string; mimeType: string; applied: string[]; metadata: Record<string, unknown> }> => {\n let currentData = data\n let currentFilename = ctx.filename\n let currentMimeType = ctx.clientMimeType\n const applied: string[] = []\n const metadata: Record<string, unknown> = {}\n\n for (const processor of uploadProcessors) {\n const processorCtx: UploadFileProcessorContext = {\n filename: currentFilename,\n clientMimeType: currentMimeType,\n totalSize: currentData.length,\n sniff: currentData,\n }\n\n if (!processor.match(processorCtx)) continue\n\n if (currentData.length > processor.maxBytes) {\n throw new Error(\"processor_input_too_large\")\n }\n\n const result = await processor.process(currentData, processorCtx)\n currentData = result.data\n if (typeof result.filename === \"string\" && result.filename.trim()) {\n currentFilename = result.filename.trim()\n }\n if (typeof result.mimeType === \"string\" && result.mimeType.trim()) {\n currentMimeType = result.mimeType.trim()\n }\n if (result.metadata && typeof result.metadata === \"object\") {\n Object.assign(metadata, result.metadata)\n }\n applied.push(processor.id)\n }\n\n return {\n data: currentData,\n filename: currentFilename,\n mimeType: currentMimeType,\n applied,\n metadata,\n }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb, models } from \"@rpcbase/db\"\nimport { GridFSBucket } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport { enqueueUploadPostProcessors } from \"../postProcessors\"\nimport { applyUploadProcessors, getMaxUploadProcessorBytes, selectUploadProcessors } from \"../processors\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n ensureUploadIndexes,\n getBucketName,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n} from \"../shared\"\n\n\nconst waitForStreamFinished = async (stream: NodeJS.WritableStream): Promise<void> => new Promise((resolve, reject) => {\n stream.once(\"finish\", resolve)\n stream.once(\"error\", reject)\n})\n\nconst writeToStream = async (stream: NodeJS.WritableStream, chunk: Buffer): Promise<void> => {\n const ok = stream.write(chunk)\n if (ok) return\n await new Promise<void>((resolve, reject) => {\n const onDrain = () => {\n cleanup()\n resolve()\n }\n\n const onError = (error: unknown) => {\n cleanup()\n reject(error)\n }\n\n const cleanup = () => {\n stream.off(\"drain\", onDrain)\n stream.off(\"error\", onError)\n }\n\n stream.on(\"drain\", onDrain)\n stream.on(\"error\", onError)\n })\n}\n\nconst abortUploadStream = async (stream: unknown): Promise<void> => {\n if (!stream) return\n if (typeof (stream as { abort?: unknown }).abort === \"function\") {\n try {\n await (stream as { abort: () => Promise<void> | void }).abort()\n return\n } catch {\n //\n }\n }\n try {\n ;(stream as { destroy?: () => void }).destroy?.()\n } catch {\n //\n }\n}\n\nexport const completeUpload: ApiHandler<Record<string, never>, Uploads.CompleteResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Uploads.CompleteResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const uploadId = String(ctx.req.params?.uploadId ?? \"\").trim()\n if (!uploadId) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_upload_id\" }\n }\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n if (!ability.can(\"update\", \"RBUploadSession\")) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const existing = await UploadSession.findOne({ $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"read\")] }).lean()\n if (!existing) {\n ctx.res.status(404)\n return { ok: false, error: \"not_found\" }\n }\n\n if (existing.status === \"done\" && existing.fileId) {\n return { ok: true, fileId: existing.fileId }\n }\n\n const locked = await UploadSession.findOneAndUpdate(\n { $and: [{ _id: uploadId }, { status: \"uploading\" }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"assembling\" }, $unset: { error: \"\" } },\n { returnDocument: \"after\" },\n ).lean()\n\n if (!locked) {\n ctx.res.status(409)\n return { ok: false, error: \"not_uploading\" }\n }\n\n await ensureUploadIndexes(UploadSession, UploadChunk)\n\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: \"filesystem_db_unavailable\" } },\n )\n ctx.res.status(500)\n return { ok: false, error: \"assembly_failed\" }\n }\n const bucketName = getBucketName()\n const bucket = new GridFSBucket(nativeDb, { bucketName })\n\n const lockedUserId = typeof locked.userId === \"string\" ? locked.userId : undefined\n const maxProcessorBytes = getMaxUploadProcessorBytes()\n const shouldBufferForProcessing = locked.totalSize <= maxProcessorBytes\n const declaredMimeType = locked.mimeType.trim().toLowerCase()\n const declaredSvg = declaredMimeType === \"image/svg+xml\" || locked.filename.trim().toLowerCase().endsWith(\".svg\")\n\n let uploadStream: NodeJS.WritableStream | null = null\n let finalFilename = locked.filename\n let finalMimeType = locked.mimeType\n let finalSize = locked.totalSize\n let inlineProcessors: string[] = []\n let finalMetadata: Record<string, unknown> = {\n uploadId,\n tenantId,\n mimeType: locked.mimeType,\n totalSize: locked.totalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n\n try {\n if (!shouldBufferForProcessing && declaredSvg) {\n throw new Error(\"svg_too_large\")\n }\n\n const cursor = UploadChunk.find({ uploadId }).sort({ index: 1 }).cursor() as unknown as AsyncIterable<UploadChunkDoc> & {\n close: () => Promise<void>\n }\n\n let expectedIndex = 0\n const chunks: Buffer[] = []\n let bufferedBytes = 0\n\n const pendingChunks: Buffer[] = []\n const sniffParts: Buffer[] = []\n let sniffBytes = 0\n\n try {\n for await (const chunkDoc of cursor) {\n if (chunkDoc.index !== expectedIndex) {\n throw new Error(\"missing_chunks\")\n }\n\n const chunk = chunkDoc.data\n\n if (shouldBufferForProcessing) {\n chunks.push(chunk)\n bufferedBytes += chunk.length\n } else if (!uploadStream) {\n pendingChunks.push(chunk)\n\n if (sniffBytes < maxProcessorBytes) {\n const slice = chunk.subarray(0, Math.min(chunk.length, maxProcessorBytes - sniffBytes))\n if (slice.length) {\n sniffParts.push(slice)\n sniffBytes += slice.length\n }\n }\n\n if (sniffBytes >= maxProcessorBytes) {\n const sniff = Buffer.concat(sniffParts, sniffBytes)\n const processors = selectUploadProcessors({\n filename: locked.filename,\n clientMimeType: locked.mimeType,\n totalSize: locked.totalSize,\n sniff,\n })\n\n if (processors.length) {\n throw new Error(processors.some((processor) => processor.id === \"sanitize-svg\") ? \"svg_too_large\" : \"processor_input_too_large\")\n }\n\n finalMetadata = {\n uploadId,\n tenantId,\n mimeType: locked.mimeType,\n totalSize: locked.totalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n uploadStream = bucket.openUploadStream(locked.filename, {\n metadata: finalMetadata,\n })\n\n for (const pending of pendingChunks) {\n await writeToStream(uploadStream, pending)\n }\n pendingChunks.length = 0\n }\n } else {\n await writeToStream(uploadStream, chunk)\n }\n\n expectedIndex += 1\n }\n } finally {\n try {\n await cursor.close()\n } catch {\n //\n }\n }\n\n if (expectedIndex !== locked.chunksTotal) {\n throw new Error(\"missing_chunks\")\n }\n\n if (shouldBufferForProcessing) {\n const assembled = Buffer.concat(chunks, bufferedBytes)\n const {\n data: processed,\n filename: processedFilename,\n mimeType: processedMimeType,\n applied,\n metadata: processedMetadata,\n } = await applyUploadProcessors(assembled, {\n filename: locked.filename,\n clientMimeType: locked.mimeType,\n })\n\n finalFilename = processedFilename\n finalMimeType = processedMimeType\n finalSize = processed.length\n inlineProcessors = applied\n finalMetadata = {\n uploadId,\n tenantId,\n mimeType: processedMimeType,\n totalSize: processed.length,\n ...(applied.length ? { processors: applied, sourceTotalSize: locked.totalSize } : {}),\n ...processedMetadata,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n uploadStream = bucket.openUploadStream(finalFilename, {\n metadata: finalMetadata,\n })\n\n const finished = waitForStreamFinished(uploadStream)\n uploadStream.end(processed)\n await finished\n } else {\n if (!uploadStream) {\n const sniff = Buffer.concat(sniffParts, sniffBytes)\n const processors = selectUploadProcessors({\n filename: locked.filename,\n clientMimeType: locked.mimeType,\n totalSize: locked.totalSize,\n sniff,\n })\n\n if (processors.length) {\n throw new Error(processors.some((processor) => processor.id === \"sanitize-svg\") ? \"svg_too_large\" : \"processor_input_too_large\")\n }\n\n finalMetadata = {\n uploadId,\n tenantId,\n mimeType: locked.mimeType,\n totalSize: locked.totalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n }\n uploadStream = bucket.openUploadStream(locked.filename, {\n metadata: finalMetadata,\n })\n\n for (const pending of pendingChunks) {\n await writeToStream(uploadStream, pending)\n }\n pendingChunks.length = 0\n }\n\n const finished = waitForStreamFinished(uploadStream)\n uploadStream.end()\n await finished\n }\n\n const fileId = String((uploadStream as unknown as { id?: unknown }).id ?? \"\")\n if (!fileId) {\n throw new Error(\"missing_file_id\")\n }\n\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"done\", fileId, filename: finalFilename, mimeType: finalMimeType, totalSize: finalSize }, $unset: { error: \"\" } },\n )\n\n await enqueueUploadPostProcessors({\n tenantId,\n uploadId,\n fileId,\n filename: finalFilename,\n mimeType: finalMimeType,\n clientMimeType: locked.mimeType,\n totalSize: finalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n inlineProcessors,\n metadata: finalMetadata,\n }).catch((error) => {\n console.error(\"Upload post processor enqueue failed\", {\n tenantId,\n uploadId,\n fileId,\n error,\n })\n })\n\n try {\n await UploadChunk.deleteMany({ uploadId })\n } catch {\n //\n }\n\n return { ok: true, fileId }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n\n await abortUploadStream(uploadStream)\n\n if (message === \"missing_chunks\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"uploading\" } },\n )\n ctx.res.status(409)\n return { ok: false, error: \"missing_chunks\" }\n }\n\n if (message === \"svg_too_large\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(413)\n return { ok: false, error: message }\n }\n\n if (message === \"heif_too_large\" || message === \"processor_input_too_large\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(413)\n return { ok: false, error: message }\n }\n\n if (message.startsWith(\"heif_decode_unsupported\")) {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(415)\n return { ok: false, error: message }\n }\n\n if (message === \"svg_invalid\" || message === \"svg_sanitize_failed\") {\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n ctx.res.status(400)\n return { ok: false, error: message }\n }\n\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n { $set: { status: \"error\", error: message } },\n )\n\n ctx.res.status(500)\n return { ok: false, error: \"assembly_failed\" }\n }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n} from \"../shared\"\n\n\nexport const getStatus: ApiHandler<Record<string, never>, Uploads.StatusResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Uploads.StatusResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const uploadId = String(ctx.req.params?.uploadId ?? \"\").trim()\n if (!uploadId) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_upload_id\" }\n }\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n if (!ability.can(\"read\", \"RBUploadSession\")) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const session = await UploadSession.findOne({ $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"read\")] }).lean()\n if (!session) {\n ctx.res.status(404)\n return { ok: false, error: \"not_found\" }\n }\n\n const receivedDocs = await UploadChunk.find(\n { uploadId },\n { index: 1, _id: 0 },\n ).sort({ index: 1 }).lean()\n\n const received = (receivedDocs as unknown as Array<{ index?: unknown }>)\n .map((doc) => (typeof doc.index === \"number\" ? doc.index : -1))\n .filter((n) => Number.isInteger(n) && n >= 0)\n\n return {\n ok: true,\n status: session.status,\n chunkSize: session.chunkSize,\n chunksTotal: session.chunksTotal,\n received,\n ...(session.fileId ? { fileId: session.fileId } : {}),\n }\n}\n","import { z } from \"zod\"\n\n\nexport const InitRoute = \"/api/rb/file-uploads\"\nexport const ChunkRoute = \"/api/rb/file-uploads/:uploadId/chunks/:index\"\nexport const StatusRoute = \"/api/rb/file-uploads/:uploadId/status\"\nexport const CompleteRoute = \"/api/rb/file-uploads/:uploadId/complete\"\n\nexport const initRequestSchema = z.object({\n filename: z.string().min(1),\n mimeType: z.string().min(1),\n isPublic: z.boolean().optional(),\n totalSize: z.number().int().min(1),\n})\n\nexport type InitRequestPayload = z.infer<typeof initRequestSchema>\n\nexport const initResponseSchema = z.object({\n ok: z.boolean(),\n error: z.string().optional(),\n uploadId: z.string().optional(),\n uploadKey: z.string().optional(),\n chunkSize: z.number().int().optional(),\n chunksTotal: z.number().int().optional(),\n})\n\nexport type InitResponsePayload = z.infer<typeof initResponseSchema>\n\nexport const statusResponseSchema = z.object({\n ok: z.boolean(),\n error: z.string().optional(),\n status: z.enum([\"uploading\", \"assembling\", \"done\", \"error\"]).optional(),\n chunkSize: z.number().int().optional(),\n chunksTotal: z.number().int().optional(),\n received: z.array(z.number().int().min(0)).optional(),\n fileId: z.string().optional(),\n})\n\nexport type StatusResponsePayload = z.infer<typeof statusResponseSchema>\n\nexport const completeResponseSchema = z.object({\n ok: z.boolean(),\n error: z.string().optional(),\n fileId: z.string().optional(),\n})\n\nexport type CompleteResponsePayload = z.infer<typeof completeResponseSchema>\n","import { randomBytes } from \"node:crypto\"\n\nimport { ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { ObjectId } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n computeSha256Hex,\n ensureUploadIndexes,\n getChunkSizeBytes,\n getModelCtx,\n getSessionTtlMs,\n getTenantId,\n getUserId,\n} from \"../shared\"\n\n\nexport const initUpload: ApiHandler<Uploads.InitRequestPayload, Uploads.InitResponsePayload, SessionUser> = async (\n payload,\n ctx,\n): Promise<Uploads.InitResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const userId = getUserId(ctx)\n\n const parsed = Uploads.initRequestSchema.safeParse(payload ?? {})\n if (!parsed.success) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_payload\" }\n }\n\n const chunkSize = getChunkSizeBytes()\n const { filename, mimeType, totalSize, isPublic } = parsed.data\n const chunksTotal = Math.ceil(totalSize / chunkSize)\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n await ensureUploadIndexes(UploadSession, UploadChunk)\n\n const uploadId = new ObjectId().toString()\n const now = Date.now()\n const expiresAt = new Date(now + getSessionTtlMs())\n\n const uploadKey = userId ? null : randomBytes(32).toString(\"base64url\")\n const ownerKeyHash = uploadKey ? computeSha256Hex(Buffer.from(uploadKey)) : undefined\n\n await UploadSession.create({\n _id: uploadId,\n ...(userId ? { userId } : {}),\n ...(ownerKeyHash ? { ownerKeyHash } : {}),\n filename,\n mimeType,\n ...(typeof isPublic === \"boolean\" ? { isPublic } : {}),\n totalSize,\n chunkSize,\n chunksTotal,\n status: \"uploading\",\n createdAt: new Date(now),\n expiresAt,\n })\n\n return {\n ok: true,\n uploadId,\n chunkSize,\n chunksTotal,\n ...(uploadKey ? { uploadKey } : {}),\n }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport type { Model } from \"mongoose\"\n\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadsAbility,\n computeSha256Hex,\n ensureUploadIndexes,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n normalizeSha256Hex,\n toBufferPayload,\n} from \"../shared\"\n\n\ntype ChunkResponsePayload = {\n ok: boolean\n error?: string\n}\n\nexport const uploadChunk: ApiHandler<Buffer, ChunkResponsePayload, SessionUser> = async (\n payload,\n ctx,\n): Promise<ChunkResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const uploadId = String(ctx.req.params?.uploadId ?? \"\").trim()\n const indexRaw = String(ctx.req.params?.index ?? \"\").trim()\n const index = Number(indexRaw)\n\n if (!uploadId || !Number.isInteger(index) || index < 0) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_chunk_ref\" }\n }\n\n const ability = buildUploadsAbility(ctx, tenantId)\n const modelCtx = getModelCtx(ctx, tenantId, ability)\n\n const [UploadSession, UploadChunk] = await Promise.all([\n models.get(\"RBUploadSession\", modelCtx) as Promise<Model<UploadSessionDoc>>,\n models.get(\"RBUploadChunk\", modelCtx) as Promise<Model<UploadChunkDoc>>,\n ])\n\n if (!ability.can(\"update\", \"RBUploadSession\")) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const session = await UploadSession.findOne({ $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] }).lean()\n if (!session) {\n ctx.res.status(404)\n return { ok: false, error: \"not_found\" }\n }\n\n if (session.status !== \"uploading\") {\n ctx.res.status(409)\n return { ok: false, error: \"not_uploading\" }\n }\n\n if (index >= session.chunksTotal) {\n ctx.res.status(400)\n return { ok: false, error: \"index_out_of_range\" }\n }\n\n const data = toBufferPayload(payload)\n if (!data) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_body\" }\n }\n\n const expectedSize = index === session.chunksTotal - 1\n ? session.totalSize - session.chunkSize * (session.chunksTotal - 1)\n : session.chunkSize\n\n if (data.length > expectedSize) {\n ctx.res.status(413)\n return { ok: false, error: \"chunk_too_large\" }\n }\n\n if (data.length !== expectedSize) {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_chunk_size\" }\n }\n\n const checksumHeader = ctx.req.get(\"X-Chunk-SHA256\")\n const sha256 = checksumHeader ? computeSha256Hex(data) : undefined\n\n if (checksumHeader) {\n const expectedSha256 = normalizeSha256Hex(checksumHeader)\n if (sha256 !== expectedSha256) {\n ctx.res.status(400)\n return { ok: false, error: \"checksum_mismatch\" }\n }\n }\n\n await ensureUploadIndexes(UploadSession, UploadChunk)\n\n await UploadChunk.updateOne(\n { uploadId, index },\n {\n $set: {\n uploadId,\n index,\n data,\n size: data.length,\n sha256,\n expiresAt: session.expiresAt,\n },\n $setOnInsert: {\n createdAt: new Date(),\n },\n },\n { upsert: true },\n )\n\n ctx.res.status(204)\n return { ok: true }\n}\n","export const rawBodyParser = ({\n limitBytes,\n maxClientBytesPerSecond,\n}: {\n limitBytes: number\n maxClientBytesPerSecond?: number | null\n}) => {\n return (req: any, res: any, next: any) => {\n const contentType = typeof req?.headers?.[\"content-type\"] === \"string\"\n ? String(req.headers[\"content-type\"])\n : \"\"\n\n if (!contentType.includes(\"application/octet-stream\")) {\n next()\n return\n }\n\n let total = 0\n const chunks: Buffer[] = []\n let done = false\n let paused = false\n let throttleTimeout: ReturnType<typeof setTimeout> | null = null\n\n const rateBytesPerSecond = typeof maxClientBytesPerSecond === \"number\" && maxClientBytesPerSecond > 0\n ? maxClientBytesPerSecond\n : null\n\n const cleanup = () => {\n req.off(\"data\", onData)\n req.off(\"end\", onEnd)\n req.off(\"error\", onError)\n req.off(\"aborted\", onAborted)\n if (throttleTimeout) {\n clearTimeout(throttleTimeout)\n throttleTimeout = null\n }\n }\n\n const finish = (error?: unknown) => {\n if (done) return\n done = true\n\n cleanup()\n\n if (error) {\n next(error)\n return\n }\n\n req.body = Buffer.concat(chunks, total)\n next()\n }\n\n const onData = (chunk: any) => {\n if (done) return\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n total += buffer.length\n\n if (total > limitBytes) {\n done = true\n cleanup()\n req.destroy()\n res.status(413).json({ ok: false, error: \"chunk_too_large\" })\n return\n }\n\n chunks.push(buffer)\n\n if (!rateBytesPerSecond) return\n\n const now = Date.now()\n const clientKey = getClientKey(req)\n const state = getClientRateState(clientKey, rateBytesPerSecond, now)\n const waitMs = consumeRateBudget(state, buffer.length, rateBytesPerSecond, now)\n\n if (waitMs > 0 && !paused) {\n paused = true\n req.pause()\n throttleTimeout = setTimeout(() => {\n throttleTimeout = null\n paused = false\n if (done) return\n try {\n req.resume()\n } catch {\n //\n }\n }, waitMs)\n }\n }\n\n const onEnd = () => finish()\n const onError = (err: unknown) => finish(err)\n const onAborted = () => finish(new Error(\"request_aborted\"))\n\n req.on(\"data\", onData)\n req.on(\"end\", onEnd)\n req.on(\"error\", onError)\n req.on(\"aborted\", onAborted)\n }\n}\n\ntype ClientRateState = {\n tokens: number\n lastRefillMs: number\n lastSeenMs: number\n}\n\nconst MAX_BURST_SECONDS = 1\nconst STALE_CLIENT_MS = 15 * 60 * 1000\n\nconst clientRateStates = new Map<string, ClientRateState>()\nlet lastCleanupMs = 0\n\nconst getClientKey = (req: any): string => {\n const rawClientIp = typeof req?.clientIp === \"string\" ? req.clientIp : \"\"\n if (rawClientIp.trim()) return rawClientIp.trim()\n const rawIp = typeof req?.ip === \"string\" ? req.ip : \"\"\n return rawIp.trim() || \"unknown\"\n}\n\nconst maybeCleanupStates = (now: number) => {\n if (now - lastCleanupMs < 60_000) return\n lastCleanupMs = now\n\n if (clientRateStates.size < 2000) return\n\n for (const [key, state] of clientRateStates) {\n if (now - state.lastSeenMs > STALE_CLIENT_MS) {\n clientRateStates.delete(key)\n }\n }\n}\n\nconst getClientRateState = (key: string, rateBytesPerSecond: number, now: number): ClientRateState => {\n maybeCleanupStates(now)\n\n const capacity = rateBytesPerSecond * MAX_BURST_SECONDS\n const existing = clientRateStates.get(key)\n if (existing) {\n existing.lastSeenMs = now\n existing.tokens = Math.min(capacity, existing.tokens)\n return existing\n }\n\n const next: ClientRateState = {\n tokens: capacity,\n lastRefillMs: now,\n lastSeenMs: now,\n }\n clientRateStates.set(key, next)\n return next\n}\n\nconst consumeRateBudget = (\n state: ClientRateState,\n bytes: number,\n rateBytesPerSecond: number,\n now: number,\n): number => {\n const capacity = rateBytesPerSecond * MAX_BURST_SECONDS\n const elapsedMs = Math.max(0, now - state.lastRefillMs)\n\n if (elapsedMs > 0) {\n state.tokens = Math.min(capacity, state.tokens + (elapsedMs * rateBytesPerSecond) / 1000)\n state.lastRefillMs = now\n }\n\n state.tokens -= bytes\n\n if (state.tokens >= 0) return 0\n return Math.ceil((-state.tokens / rateBytesPerSecond) * 1000)\n}\n","import { Api } from \"@rpcbase/api\"\n\nimport { completeUpload } from \"./handlers/completeUpload\"\nimport { getStatus } from \"./handlers/getStatus\"\nimport { initUpload } from \"./handlers/initUpload\"\nimport { uploadChunk } from \"./handlers/uploadChunk\"\nimport { rawBodyParser } from \"./middleware/rawBodyParser\"\nimport { getChunkSizeBytes, getMaxClientUploadBytesPerSecond, getRawBodyLimitBytes, type SessionUser } from \"./shared\"\n\nimport * as Uploads from \"./index\"\n\n\nexport default (api: Api<SessionUser>) => {\n const chunkSizeBytes = getChunkSizeBytes()\n api.use(\n Uploads.InitRoute,\n rawBodyParser({\n limitBytes: getRawBodyLimitBytes(chunkSizeBytes),\n maxClientBytesPerSecond: getMaxClientUploadBytesPerSecond(),\n }),\n )\n\n api.post(Uploads.InitRoute, initUpload)\n api.put(Uploads.ChunkRoute, uploadChunk)\n api.get(Uploads.StatusRoute, getStatus)\n api.post(Uploads.CompleteRoute, completeUpload)\n}\n"],"names":["MAX_SVG_BYTES","window","JSDOM","DOMPurify","createDOMPurify","normalizeForSniff","raw","replace","trimStart","looksLikeSvgText","text","normalized","startsWith","test","looksLikeSvg","sniff","toString","sanitizeSvg","svg","sanitize","USE_PROFILES","svgFilters","sanitizeSvgProcessor","id","maxBytes","match","process","data","length","Error","svgText","sanitized","trim","sanitizedBuffer","Buffer","from","mimeType","uploadProcessors","Object","freeze","convertHeifToWebpProcessor","getMaxUploadProcessorBytes","reduce","max","processor","Math","selectUploadProcessors","ctx","filter","applyUploadProcessors","currentData","currentFilename","filename","currentMimeType","clientMimeType","applied","metadata","processorCtx","totalSize","result","assign","push","waitForStreamFinished","stream","Promise","resolve","reject","once","writeToStream","chunk","ok","write","onDrain","cleanup","onError","error","off","on","abortUploadStream","abort","destroy","completeUpload","_payload","tenantId","getTenantId","res","status","uploadId","String","req","params","ability","buildUploadsAbility","modelCtx","getModelCtx","UploadSession","UploadChunk","all","models","get","can","existing","findOne","$and","_id","getUploadSessionAccessQuery","lean","fileId","locked","findOneAndUpdate","$set","$unset","returnDocument","ensureUploadIndexes","fsDb","getTenantFilesystemDb","nativeDb","db","updateOne","bucketName","getBucketName","bucket","GridFSBucket","lockedUserId","userId","undefined","maxProcessorBytes","shouldBufferForProcessing","declaredMimeType","toLowerCase","declaredSvg","endsWith","uploadStream","finalFilename","finalMimeType","finalSize","inlineProcessors","finalMetadata","isPublic","ownerKeyHash","cursor","find","sort","index","expectedIndex","chunks","bufferedBytes","pendingChunks","sniffParts","sniffBytes","chunkDoc","slice","subarray","min","concat","processors","some","openUploadStream","pending","close","chunksTotal","assembled","processed","processedFilename","processedMimeType","processedMetadata","sourceTotalSize","finished","end","enqueueUploadPostProcessors","catch","console","deleteMany","message","getStatus","session","receivedDocs","received","map","doc","n","Number","isInteger","chunkSize","InitRoute","ChunkRoute","StatusRoute","CompleteRoute","initRequestSchema","z","string","boolean","optional","number","int","uploadKey","initUpload","payload","getUserId","parsed","Uploads","safeParse","success","getChunkSizeBytes","ceil","ObjectId","now","Date","expiresAt","getSessionTtlMs","randomBytes","computeSha256Hex","create","createdAt","uploadChunk","indexRaw","toBufferPayload","expectedSize","checksumHeader","sha256","expectedSha256","normalizeSha256Hex","size","$setOnInsert","upsert","rawBodyParser","limitBytes","maxClientBytesPerSecond","next","contentType","headers","includes","total","done","paused","throttleTimeout","rateBytesPerSecond","onData","onEnd","onAborted","clearTimeout","finish","body","buffer","isBuffer","json","clientKey","getClientKey","state","getClientRateState","waitMs","consumeRateBudget","pause","setTimeout","resume","err","MAX_BURST_SECONDS","STALE_CLIENT_MS","clientRateStates","Map","lastCleanupMs","rawClientIp","clientIp","rawIp","ip","maybeCleanupStates","key","lastSeenMs","delete","capacity","tokens","lastRefillMs","set","bytes","elapsedMs","api","chunkSizeBytes","use","getRawBodyLimitBytes","getMaxClientUploadBytesPerSecond","post","put"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { models } from "@rpcbase/db";
|
|
2
2
|
import { buildAbilityFromSession, getAccessibleByQuery } from "@rpcbase/db/acl";
|
|
3
3
|
import { createNotification, sendNotificationsDigestForUser } from "./notifications.js";
|
|
4
|
-
import { o as object, b as boolean, n as number, a as array, s as string, r as record, _ as _enum, u as unknown } from "./schemas-
|
|
4
|
+
import { o as object, b as boolean, n as number, a as array, s as string, r as record, _ as _enum, u as unknown } from "./schemas-Drf83ni9.js";
|
|
5
5
|
const getSessionUser = (ctx) => {
|
|
6
6
|
const rawSessionUser = ctx.req.session?.user;
|
|
7
7
|
const userId = typeof rawSessionUser?.id === "string" ? rawSessionUser.id.trim() : "";
|
|
@@ -660,4 +660,4 @@ const handler = (api) => {
|
|
|
660
660
|
export {
|
|
661
661
|
handler as default
|
|
662
662
|
};
|
|
663
|
-
//# sourceMappingURL=handler-
|
|
663
|
+
//# sourceMappingURL=handler-D58VS91o.js.map
|