@rpcbase/server 0.608.0 → 0.610.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/{fileReferences-Ca0tivFd.js → fileReferences-C6gDuRO3.js} +2 -2
- package/dist/{fileReferences-Ca0tivFd.js.map → fileReferences-C6gDuRO3.js.map} +1 -1
- package/dist/{handler-C63qGUhq.js → handler-B61RAoQK.js} +3 -3
- package/dist/{handler-C63qGUhq.js.map → handler-B61RAoQK.js.map} +1 -1
- package/dist/{handler-BgcIWWMc.js → handler-CnYlcu-N.js} +18 -8
- package/dist/handler-CnYlcu-N.js.map +1 -0
- package/dist/{handler-CqSlSCdd.js → handler-D9cQBJyz.js} +7 -7
- package/dist/handler-D9cQBJyz.js.map +1 -0
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/initServer.d.ts +19 -3
- package/dist/initServer.d.ts.map +1 -1
- package/dist/notifications/api/notifications/handler.d.ts.map +1 -1
- package/dist/notifications/digest.d.ts.map +1 -1
- package/dist/notifications.js +18 -15
- package/dist/notifications.js.map +1 -1
- package/dist/{queryWindow-DP8zD0lb.js → queryWindow-DNQxuY_M.js} +6 -5
- package/dist/queryWindow-DNQxuY_M.js.map +1 -0
- package/dist/rts/index.js +1 -1
- package/dist/rts/queryExecutor.d.ts.map +1 -1
- package/dist/{shared-Zt17vuAT.js → shared-BX-oLc9O.js} +4 -2
- package/dist/shared-BX-oLc9O.js.map +1 -0
- package/dist/uploads/api/file-uploads/handlers/initUpload.d.ts.map +1 -1
- package/dist/uploads/api/file-uploads/shared.d.ts +1 -2
- package/dist/uploads/api/file-uploads/shared.d.ts.map +1 -1
- package/dist/uploads/worker.js +1 -1
- package/dist/uploads.js +2 -2
- package/package.json +1 -1
- package/dist/handler-BgcIWWMc.js.map +0 -1
- package/dist/handler-CqSlSCdd.js.map +0 -1
- package/dist/queryWindow-DP8zD0lb.js.map +0 -1
- package/dist/shared-Zt17vuAT.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-D9cQBJyz.js","names":["JSDOM","createDOMPurify","UploadFileProcessor","MAX_SVG_BYTES","window","DOMPurify","normalizeForSniff","raw","replace","trimStart","looksLikeSvgText","text","normalized","startsWith","test","looksLikeSvg","sniff","Buffer","toString","sanitizeSvg","svg","sanitize","USE_PROFILES","svgFilters","sanitizeSvgProcessor","id","maxBytes","match","process","data","mimeType","length","Error","svgText","sanitized","trim","sanitizedBuffer","from","convertHeifToWebpProcessor","sanitizeSvgProcessor","UploadFileProcessorContext","filename","clientMimeType","totalSize","sniff","Buffer","UploadFileProcessorResult","data","mimeType","metadata","Record","UploadFileProcessor","id","maxBytes","match","ctx","process","Promise","uploadProcessors","Object","freeze","getMaxUploadProcessorBytes","reduce","max","processor","Math","selectUploadProcessors","filter","applyUploadProcessors","Omit","applied","currentData","currentFilename","currentMimeType","processorCtx","length","Error","result","trim","assign","push","createHash","ApiHandler","getTenantFilesystemDb","models","GridFSBucket","ObjectId","Model","Uploads","claimUploadFileReference","createUploadFileReference","enqueueUploadPostProcessors","applyUploadProcessors","getMaxUploadProcessorBytes","selectUploadProcessors","SessionUser","UploadChunkDoc","UploadSessionDoc","buildUploadAccessMetadata","buildUploadsAbility","ensureFilesystemUploadIndexes","ensureUploadIndexes","getBucketName","getModelCtx","getUploadSessionAccessQuery","getTenantId","resolveUploadAccessScope","waitForStreamFinished","stream","NodeJS","WritableStream","Promise","resolve","reject","once","writeToStream","chunk","Buffer","ok","write","onDrain","cleanup","onError","error","off","on","abortUploadStream","abort","destroy","isDuplicateKeyError","code","completeUpload","Record","CompleteResponsePayload","_payload","ctx","tenantId","res","status","uploadId","String","req","params","trim","ability","modelCtx","UploadSession","UploadChunk","all","get","can","existing","findOne","$and","_id","lean","fileId","deduplicated","locked","findOneAndUpdate","$set","$unset","returnDocument","fsDb","nativeDb","db","updateOne","bucketName","bucket","deduplication","lockedUserId","userId","undefined","accessMetadata","unclaimedDeduplication","namespace","algorithm","hash","pipelineVersion","createFileMetadata","storedMimeType","storedSize","additional","mimeType","totalSize","maxProcessorBytes","shouldBufferForProcessing","declaredMimeType","toLowerCase","declaredSvg","filename","endsWith","uploadStream","finalFilename","finalMimeType","finalSize","inlineProcessors","finalMetadata","Error","cursor","find","sort","index","AsyncIterable","close","expectedIndex","sourceBytes","sourceHasher","chunks","bufferedBytes","pendingChunks","sniffParts","sniffBytes","chunkDoc","data","length","update","push","slice","subarray","Math","min","sniff","concat","processors","clientMimeType","some","processor","id","openUploadStream","metadata","pending","chunksTotal","digest","assembled","processed","processedFilename","processedMimeType","applied","processedMetadata","sourceTotalSize","finished","end","uploadedBlobId","resolvedBlobId","returnedFileId","filesCollection","collection","uploadedBlobObjectId","referenceOptions","accessScope","isPublic","ownerKeyHash","lastDuplicateKeyError","attempt","blobId","canonicalFile","key","projection","canonicalReferenceId","deduplicationKey","delete","toHexString","catch","console","deleteMany","message","startsWith","ApiHandler","models","Model","Uploads","SessionUser","UploadChunkDoc","UploadSessionDoc","buildUploadsAbility","getModelCtx","getUploadSessionAccessQuery","getTenantId","getStatus","Record","StatusResponsePayload","_payload","ctx","Promise","tenantId","res","status","ok","error","uploadId","String","req","params","trim","ability","modelCtx","UploadSession","UploadChunk","all","get","can","session","findOne","$and","_id","lean","receivedDocs","find","index","sort","received","Array","map","doc","filter","n","Number","isInteger","chunkSize","chunksTotal","fileId","z","InitRoute","ChunkRoute","StatusRoute","CompleteRoute","uploadAccessScopeSchema","enum","uploadDeduplicationSchema","object","namespace","string","trim","min","max","regex","algorithm","literal","hash","initRequestSchema","filename","mimeType","isPublic","boolean","optional","accessScope","deduplication","totalSize","number","int","superRefine","value","ctx","undefined","addIssue","code","message","path","InitRequestPayload","infer","initResponseSchema","ok","error","uploadId","uploadKey","chunkSize","chunksTotal","fileId","deduplicated","InitResponsePayload","statusResponseSchema","status","received","array","StatusResponsePayload","completeResponseSchema","CompleteResponsePayload","randomBytes","ApiHandler","getTenantFilesystemDb","models","ObjectId","Model","Uploads","createUploadFileReference","SessionUser","UploadSessionDoc","buildUploadDeduplication","buildUploadsAbility","computeSha256Hex","ensureFilesystemUploadIndexes","ensureUploadIndexes","getBucketName","getChunkSizeBytes","getModelCtx","getSessionTtlMs","getTenantId","getUserId","resolveUploadAccessScope","initUpload","InitRequestPayload","InitResponsePayload","payload","ctx","Promise","tenantId","res","status","ok","error","userId","parsed","initRequestSchema","safeParse","success","filename","mimeType","totalSize","isPublic","deduplication","requestedDeduplication","data","accessScope","uploadKey","toString","ownerKeyHash","Buffer","from","undefined","namespace","hash","fsDb","nativeDb","db","bucketName","existingFile","collection","findOne","key","projection","_id","length","metadata","existingMetadata","Record","existingMimeType","existingSize","fileId","blobId","deduplicationKey","deduplicated","chunkSize","chunksTotal","Math","ceil","ability","modelCtx","UploadSession","get","uploadId","now","Date","expiresAt","create","createdAt","ApiHandler","models","Model","SessionUser","UploadChunkDoc","UploadSessionDoc","buildUploadsAbility","computeSha256Hex","ensureUploadIndexes","getModelCtx","getUploadSessionAccessQuery","getTenantId","normalizeSha256Hex","toBufferPayload","ChunkResponsePayload","ok","error","uploadChunk","Buffer","payload","ctx","Promise","tenantId","res","status","uploadId","String","req","params","trim","indexRaw","index","Number","isInteger","ability","modelCtx","UploadSession","UploadChunk","all","get","can","session","findOne","$and","_id","lean","chunksTotal","data","expectedSize","totalSize","chunkSize","length","checksumHeader","sha256","undefined","expectedSha256","updateOne","$set","size","expiresAt","$setOnInsert","createdAt","Date","upsert","rawBodyParser","limitBytes","maxClientBytesPerSecond","req","res","next","contentType","headers","String","includes","total","chunks","Buffer","done","paused","throttleTimeout","ReturnType","setTimeout","rateBytesPerSecond","cleanup","off","onData","onEnd","onError","onAborted","clearTimeout","finish","error","body","concat","chunk","buffer","isBuffer","from","length","destroy","status","json","ok","push","now","Date","clientKey","getClientKey","state","getClientRateState","waitMs","consumeRateBudget","pause","resume","err","Error","on","ClientRateState","tokens","lastRefillMs","lastSeenMs","MAX_BURST_SECONDS","STALE_CLIENT_MS","clientRateStates","Map","lastCleanupMs","rawClientIp","clientIp","trim","rawIp","ip","maybeCleanupStates","size","key","delete","capacity","existing","get","Math","min","set","bytes","elapsedMs","max","ceil","Api","completeUpload","getStatus","initUpload","uploadChunk","rawBodyParser","getChunkSizeBytes","getMaxClientUploadBytesPerSecond","getRawBodyLimitBytes","SessionUser","Uploads","api","chunkSizeBytes","use","InitRoute","limitBytes","maxClientBytesPerSecond","post","put","ChunkRoute","get","StatusRoute","CompleteRoute"],"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 { createHash } from \"node:crypto\"\n\nimport { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb, models } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport {\n claimUploadFileReference,\n createUploadFileReference,\n} from \"../fileReferences\"\nimport { enqueueUploadPostProcessors } from \"../postProcessors\"\nimport { applyUploadProcessors, getMaxUploadProcessorBytes, selectUploadProcessors } from \"../processors\"\nimport {\n type SessionUser,\n type UploadChunkDoc,\n type UploadSessionDoc,\n buildUploadAccessMetadata,\n buildUploadsAbility,\n ensureFilesystemUploadIndexes,\n ensureUploadIndexes,\n getBucketName,\n getModelCtx,\n getUploadSessionAccessQuery,\n getTenantId,\n resolveUploadAccessScope,\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\nconst isDuplicateKeyError = (error: unknown): boolean => (\n error !== null &&\n typeof error === \"object\" &&\n \"code\" in error &&\n (error as { code?: unknown }).code === 11000\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 {\n ok: true,\n fileId: existing.fileId,\n ...(existing.deduplicated ? { deduplicated: true } : {}),\n }\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(modelCtx)\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 if (locked.deduplication) {\n await ensureFilesystemUploadIndexes(nativeDb, bucketName)\n }\n\n const lockedUserId = typeof locked.userId === \"string\" ? locked.userId : undefined\n const accessMetadata = buildUploadAccessMetadata(locked)\n const unclaimedDeduplication = locked.deduplication\n ? {\n deduplication: {\n namespace: locked.deduplication.namespace,\n algorithm: locked.deduplication.algorithm,\n hash: locked.deduplication.hash,\n pipelineVersion: locked.deduplication.pipelineVersion,\n },\n }\n : {}\n const createFileMetadata = (\n storedMimeType: string,\n storedSize: number,\n additional: Record<string, unknown> = {},\n ): Record<string, unknown> => ({\n uploadId,\n tenantId,\n mimeType: storedMimeType,\n totalSize: storedSize,\n ...additional,\n ...accessMetadata,\n ...unclaimedDeduplication,\n })\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 = createFileMetadata(locked.mimeType, locked.totalSize)\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 let sourceBytes = 0\n const sourceHasher = locked.deduplication ? createHash(\"sha256\") : null\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 sourceBytes += chunk.length\n sourceHasher?.update(chunk)\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 = createFileMetadata(locked.mimeType, locked.totalSize)\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 (sourceBytes !== locked.totalSize) {\n throw new Error(\"invalid_source_size\")\n }\n\n if (sourceHasher && sourceHasher.digest(\"hex\") !== locked.deduplication?.hash) {\n throw new Error(\"content_hash_mismatch\")\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 = createFileMetadata(processedMimeType, processed.length, {\n ...(applied.length ? { processors: applied, sourceTotalSize: locked.totalSize } : {}),\n ...processedMetadata,\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 = createFileMetadata(locked.mimeType, locked.totalSize)\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 uploadedBlobId = String((uploadStream as unknown as { id?: unknown }).id ?? \"\")\n if (!uploadedBlobId) {\n throw new Error(\"missing_file_id\")\n }\n\n let resolvedBlobId = uploadedBlobId\n let deduplicated = false\n let returnedFileId: string | null = locked.deduplication ? null : uploadedBlobId\n\n if (locked.deduplication) {\n const filesCollection = nativeDb.collection(`${bucketName}.files`)\n const uploadedBlobObjectId = new ObjectId(uploadedBlobId)\n const referenceOptions = {\n tenantId,\n bucketName,\n filename: finalFilename,\n mimeType: finalMimeType,\n totalSize: finalSize,\n accessScope: resolveUploadAccessScope({\n accessScope: locked.accessScope,\n isPublic: locked.isPublic,\n }),\n ...(lockedUserId ? { userId: lockedUserId } : {}),\n ...(typeof locked.ownerKeyHash === \"string\" ? { ownerKeyHash: locked.ownerKeyHash } : {}),\n }\n let lastDuplicateKeyError: unknown = null\n\n for (let attempt = 0; attempt < 3 && !returnedFileId; attempt += 1) {\n try {\n returnedFileId = await claimUploadFileReference({\n ...referenceOptions,\n blobId: uploadedBlobObjectId,\n deduplication: locked.deduplication,\n })\n if (!returnedFileId) throw new Error(\"missing_file_id\")\n finalMetadata = {\n ...finalMetadata,\n deduplication: locked.deduplication,\n }\n break\n } catch (error) {\n if (!isDuplicateKeyError(error)) throw error\n lastDuplicateKeyError = error\n }\n\n const canonicalFile = await filesCollection.findOne(\n { \"metadata.deduplication.key\": locked.deduplication.key },\n { projection: { _id: 1 } },\n )\n if (!(canonicalFile?._id instanceof ObjectId)) continue\n\n const canonicalReferenceId = await createUploadFileReference({\n ...referenceOptions,\n blobId: canonicalFile._id,\n deduplicationKey: locked.deduplication.key,\n })\n if (!canonicalReferenceId) continue\n\n await bucket.delete(uploadedBlobObjectId)\n resolvedBlobId = canonicalFile._id.toHexString()\n returnedFileId = canonicalReferenceId\n deduplicated = true\n }\n\n if (!returnedFileId) {\n await bucket.delete(uploadedBlobObjectId)\n if (lastDuplicateKeyError) throw lastDuplicateKeyError\n }\n }\n if (!returnedFileId) {\n throw new Error(\"canonical_file_missing\")\n }\n\n await UploadSession.updateOne(\n { $and: [{ _id: uploadId }, getUploadSessionAccessQuery(ability, \"update\")] },\n {\n $set: {\n status: \"done\",\n fileId: returnedFileId,\n filename: finalFilename,\n mimeType: finalMimeType,\n totalSize: finalSize,\n ...(deduplicated ? { deduplicated: true } : {}),\n },\n $unset: { error: \"\" },\n },\n )\n\n if (!deduplicated) {\n await enqueueUploadPostProcessors({\n tenantId,\n uploadId,\n fileId: resolvedBlobId,\n filename: finalFilename,\n mimeType: finalMimeType,\n clientMimeType: locked.mimeType,\n totalSize: finalSize,\n ...(typeof locked.isPublic === \"boolean\" ? { isPublic: locked.isPublic } : {}),\n ...(locked.accessScope ? { accessScope: locked.accessScope } : {}),\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: resolvedBlobId,\n error,\n })\n })\n }\n\n try {\n await UploadChunk.deleteMany({ uploadId })\n } catch {\n //\n }\n\n return {\n ok: true,\n fileId: returnedFileId,\n ...(deduplicated ? { deduplicated: true } : {}),\n }\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 === \"content_hash_mismatch\") {\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 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 uploadAccessScopeSchema = z.enum([\"owner\", \"tenant\", \"public\"])\n\nexport const uploadDeduplicationSchema = z.object({\n namespace: z.string().trim().min(1).max(128).regex(/^[a-z0-9][a-z0-9._:-]*$/i),\n algorithm: z.literal(\"sha256\"),\n hash: z.string().trim().regex(/^[a-f0-9]{64}$/i),\n})\n\nexport const initRequestSchema = z.object({\n filename: z.string().min(1),\n mimeType: z.string().min(1),\n isPublic: z.boolean().optional(),\n accessScope: uploadAccessScopeSchema.optional(),\n deduplication: uploadDeduplicationSchema.optional(),\n totalSize: z.number().int().min(1),\n}).superRefine((value, ctx) => {\n if (value.accessScope !== undefined && value.isPublic !== undefined) {\n ctx.addIssue({\n code: \"custom\",\n message: \"accessScope and isPublic are mutually exclusive\",\n path: [\"accessScope\"],\n })\n }\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 fileId: z.string().optional(),\n deduplicated: z.boolean().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 deduplicated: z.boolean().optional(),\n})\n\nexport type CompleteResponsePayload = z.infer<typeof completeResponseSchema>\n","import { randomBytes } from \"node:crypto\"\n\nimport { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb, models } from \"@rpcbase/db\"\nimport { ObjectId } from \"mongodb\"\nimport type { Model } from \"mongoose\"\n\nimport * as Uploads from \"../index\"\nimport { createUploadFileReference } from \"../fileReferences\"\nimport {\n type SessionUser,\n type UploadSessionDoc,\n buildUploadDeduplication,\n buildUploadsAbility,\n computeSha256Hex,\n ensureFilesystemUploadIndexes,\n ensureUploadIndexes,\n getBucketName,\n getChunkSizeBytes,\n getModelCtx,\n getSessionTtlMs,\n getTenantId,\n getUserId,\n resolveUploadAccessScope,\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 { filename, mimeType, totalSize, isPublic, deduplication: requestedDeduplication } = parsed.data\n const accessScope = resolveUploadAccessScope({ accessScope: parsed.data.accessScope, isPublic })\n if (accessScope === \"tenant\" && !userId) {\n ctx.res.status(401)\n return { ok: false, error: \"authentication_required\" }\n }\n\n const uploadKey = userId ? null : randomBytes(32).toString(\"base64url\")\n const ownerKeyHash = uploadKey ? computeSha256Hex(Buffer.from(uploadKey)) : undefined\n const deduplication = requestedDeduplication\n ? buildUploadDeduplication({\n namespace: requestedDeduplication.namespace,\n hash: requestedDeduplication.hash,\n filename,\n mimeType,\n totalSize,\n accessScope,\n ...(userId ? { userId } : {}),\n ...(ownerKeyHash ? { ownerKeyHash } : {}),\n })\n : null\n\n if (requestedDeduplication && !deduplication) {\n ctx.res.status(400)\n return { ok: false, error: \"deduplication_unavailable\" }\n }\n\n if (deduplication) {\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n ctx.res.status(500)\n return { ok: false, error: \"filesystem_db_unavailable\" }\n }\n\n const bucketName = getBucketName()\n await ensureFilesystemUploadIndexes(nativeDb, bucketName)\n const existingFile = await nativeDb.collection(`${bucketName}.files`).findOne(\n { \"metadata.deduplication.key\": deduplication.key },\n { projection: { _id: 1, filename: 1, length: 1, metadata: 1 } },\n )\n if (existingFile?._id instanceof ObjectId) {\n const existingMetadata = existingFile.metadata && typeof existingFile.metadata === \"object\"\n ? existingFile.metadata as Record<string, unknown>\n : {}\n const existingMimeType = typeof existingMetadata.mimeType === \"string\"\n ? existingMetadata.mimeType\n : mimeType\n const existingSize = typeof existingFile.length === \"number\"\n ? existingFile.length\n : totalSize\n const fileId = await createUploadFileReference({\n tenantId,\n bucketName,\n blobId: existingFile._id,\n filename,\n mimeType: existingMimeType,\n totalSize: existingSize,\n accessScope,\n ...(userId ? { userId } : {}),\n ...(ownerKeyHash ? { ownerKeyHash } : {}),\n deduplicationKey: deduplication.key,\n })\n if (fileId) {\n return {\n ok: true,\n fileId,\n deduplicated: true,\n ...(uploadKey ? { uploadKey } : {}),\n }\n }\n }\n }\n\n const chunkSize = getChunkSizeBytes()\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 = await models.get(\n \"RBUploadSession\",\n modelCtx,\n ) as Model<UploadSessionDoc>\n\n await ensureUploadIndexes(modelCtx)\n\n const uploadId = new ObjectId().toString()\n const now = Date.now()\n const expiresAt = new Date(now + getSessionTtlMs())\n\n await UploadSession.create({\n _id: uploadId,\n ...(userId ? { userId } : {}),\n ...(ownerKeyHash ? { ownerKeyHash } : {}),\n filename,\n mimeType,\n isPublic: accessScope === \"public\",\n accessScope,\n ...(deduplication ? { deduplication } : {}),\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(modelCtx)\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"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as __commonJSMin, i as sendEmail, n as configureEmailTransport, o as __toESM, r as createResendEmailTransport, t as EmailDeliveryError } from "./email-CK8NNDQY.js";
|
|
2
|
-
import { c as runRtsQuery, d as getDerivedKey, i as normalizeRtsQueryOptions, n as buildRtsAbilityFromRequest, o as resolveRtsRequestTenantId, r as isRtsRequestAuthorized, s as runRtsCount, t as RTS_QUERY_WINDOW_MAX_COUNT, u as syncAuthenticatedSessionMiddleware } from "./queryWindow-
|
|
2
|
+
import { c as runRtsQuery, d as getDerivedKey, i as normalizeRtsQueryOptions, n as buildRtsAbilityFromRequest, o as resolveRtsRequestTenantId, r as isRtsRequestAuthorized, s as runRtsCount, t as RTS_QUERY_WINDOW_MAX_COUNT, u as syncAuthenticatedSessionMiddleware } from "./queryWindow-DNQxuY_M.js";
|
|
3
3
|
import session from "express-session";
|
|
4
4
|
import { RedisStore } from "connect-redis";
|
|
5
5
|
import MongoStore from "connect-mongo";
|
|
@@ -4354,7 +4354,7 @@ var createRedisSessionStore = async (redisUrl) => {
|
|
|
4354
4354
|
ttl: SESSION_MAX_AGE_S
|
|
4355
4355
|
});
|
|
4356
4356
|
};
|
|
4357
|
-
|
|
4357
|
+
async function initServer(app, serverEnv, options = {}) {
|
|
4358
4358
|
bindShutdownSignals();
|
|
4359
4359
|
await initApiClient({ app });
|
|
4360
4360
|
const replicaSetInitPromise = checkInitReplicaSet(serverEnv).catch((error) => {
|
|
@@ -4379,6 +4379,8 @@ var initServer = async (app, serverEnv) => {
|
|
|
4379
4379
|
bindExpressErrorHandler(app, posthog);
|
|
4380
4380
|
ensurePosthogShutdown();
|
|
4381
4381
|
}
|
|
4382
|
+
if (!isProduction$1) registerDevCoverageEndpoints(app);
|
|
4383
|
+
if (options.sessions === false) return { sessionMiddleware: void 0 };
|
|
4382
4384
|
if (!serverEnv.MASTER_KEY) throw new Error("MASTER_KEY must be defined to derive the session secret");
|
|
4383
4385
|
if (!serverEnv.RB_PROXY_SHARED_SECRET?.trim()) throw new Error("RB_PROXY_SHARED_SECRET must be defined");
|
|
4384
4386
|
const sessionSecret = getDerivedKey(serverEnv.MASTER_KEY, "express_session_key");
|
|
@@ -4400,11 +4402,10 @@ var initServer = async (app, serverEnv) => {
|
|
|
4400
4402
|
};
|
|
4401
4403
|
if (isProduction$1) sessionConfig.cookie.secure = "auto";
|
|
4402
4404
|
const sessionMiddleware = session(sessionConfig);
|
|
4403
|
-
if (!isProduction$1) registerDevCoverageEndpoints(app);
|
|
4404
4405
|
app.use(isProduction$1 ? sessionMiddleware : developmentSessionMiddleware(sessionMiddleware));
|
|
4405
4406
|
app.use(syncAuthenticatedSessionMiddleware);
|
|
4406
4407
|
return { sessionMiddleware };
|
|
4407
|
-
}
|
|
4408
|
+
}
|
|
4408
4409
|
//#endregion
|
|
4409
4410
|
//#region src/hashPassword.ts
|
|
4410
4411
|
async function hashPassword(password, salt) {
|