@rpcbase/server 0.605.0 → 0.606.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.
@@ -2,7 +2,7 @@ import { a as number, c as string, n as array, o as object, r as boolean, t as _
2
2
  import { ZRBRtsChangeOp, models } from "@rpcbase/db";
3
3
  import { buildAbilityFromSession } from "@rpcbase/db/acl";
4
4
  //#region src/rts/api/changes/index.ts
5
- var Route = "/api/rb/rts/changes";
5
+ var ROUTE = "/api/rb/rts/changes";
6
6
  var requestSchema = object({
7
7
  sinceSeq: number().int().min(0).default(0),
8
8
  limit: number().int().min(1).max(5e3).default(2e3),
@@ -111,9 +111,9 @@ var changesHandler = async (payload, ctx) => {
111
111
  };
112
112
  };
113
113
  var handler_default = (api) => {
114
- api.post(Route, changesHandler);
114
+ api.post(ROUTE, changesHandler);
115
115
  };
116
116
  //#endregion
117
117
  export { handler_default as default };
118
118
 
119
- //# sourceMappingURL=handler-BUTqP40B.js.map
119
+ //# sourceMappingURL=handler-BhpoyxI4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler-BUTqP40B.js","names":["z","Route","requestSchema","object","sinceSeq","number","int","min","default","limit","max","modelNames","array","string","optional","RequestPayload","infer","responseSchema","ok","boolean","needsFullResync","earliestSeq","latestSeq","changes","seq","modelName","op","enum","docId","ResponsePayload","Api","ApiHandler","Ctx","models","ZRBRtsChangeOp","IRBRtsChange","IRBRtsCounter","LoadModelCtx","buildAbilityFromSession","AclSubjectType","Model","Changes","SessionUser","id","currentTenantId","signedInTenants","RtsCounterDoc","RtsChangeDoc","getTenantId","ctx","sessionTenantId","req","session","user","trim","ensureAuthorized","tenantId","userId","normalizedCurrentTenantId","getModelCtx","_ctx","isRtsChangeRecord","value","obj","Partial","isOp","safeParse","op","success","seq","modelName","changesHandler","RequestPayload","ResponsePayload","payload","Promise","parsed","requestSchema","res","status","ok","latestSeq","changes","ability","modelCtx","RtsChange","RtsCounter","all","get","counter","findOne","_id","lean","Number","sinceSeq","limit","modelNames","data","requestedModelNames","Array","isArray","length","map","m","String","filter","Boolean","allowedModelNames","can","from","Set","distinct","earliestSeq","earliest","$in","sort","undefined","needsFullResync","selector","Record","$gt","find","docId","c","api","post","Route"],"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"],"mappings":"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"}
1
+ {"version":3,"file":"handler-BhpoyxI4.js","names":["z","ROUTE","requestSchema","object","sinceSeq","number","int","min","default","limit","max","modelNames","array","string","optional","RequestPayload","infer","responseSchema","ok","boolean","needsFullResync","earliestSeq","latestSeq","changes","seq","modelName","op","enum","docId","ResponsePayload","Api","ApiHandler","Ctx","models","ZRBRtsChangeOp","IRBRtsChange","IRBRtsCounter","LoadModelCtx","buildAbilityFromSession","AclSubjectType","Model","Changes","SessionUser","id","currentTenantId","signedInTenants","RtsCounterDoc","RtsChangeDoc","getTenantId","ctx","sessionTenantId","req","session","user","trim","ensureAuthorized","tenantId","userId","normalizedCurrentTenantId","getModelCtx","_ctx","isRtsChangeRecord","value","obj","Partial","isOp","safeParse","op","success","seq","modelName","changesHandler","RequestPayload","ResponsePayload","payload","Promise","parsed","requestSchema","res","status","ok","latestSeq","changes","ability","modelCtx","RtsChange","RtsCounter","all","get","counter","findOne","_id","lean","Number","sinceSeq","limit","modelNames","data","requestedModelNames","Array","isArray","length","map","m","String","filter","Boolean","allowedModelNames","can","from","Set","distinct","earliestSeq","earliest","$in","sort","undefined","needsFullResync","selector","Record","$gt","find","docId","c","api","post","ROUTE"],"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"],"mappings":"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"}
@@ -275,16 +275,14 @@ var getFile = async (_payload, ctx) => {
275
275
  };
276
276
  //#endregion
277
277
  //#region src/uploads/api/files/index.ts
278
- var Route = "/api/rb/files/:fileId";
279
- var GetRoute = Route;
280
- var DeleteRoute = Route;
278
+ var ROUTE = "/api/rb/files/:fileId";
281
279
  //#endregion
282
280
  //#region src/uploads/api/files/handler.ts
283
281
  var handler_default = (api) => {
284
- api.get(GetRoute, getFile);
285
- api.delete(DeleteRoute, deleteFile);
282
+ api.get(ROUTE, getFile);
283
+ api.delete(ROUTE, deleteFile);
286
284
  };
287
285
  //#endregion
288
286
  export { handler_default as default };
289
287
 
290
- //# sourceMappingURL=handler-LRLmxrsg.js.map
288
+ //# sourceMappingURL=handler-C63qGUhq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-C63qGUhq.js","names":["ApiHandler","getTenantFilesystemDb","GridFSBucket","ObjectId","deleteUploadFileReference","findUploadFileReference","getBucketName","getTenantId","getUploadKeyHash","getUserId","resolveStoredUploadAccessScope","SessionUser","DeleteResponsePayload","ok","error","resolveHeaderString","value","normalized","trim","isFileNotFoundError","message","Error","String","includes","deleteGridFsFile","bucket","fileId","Promise","delete","deleteFile","Record","_payload","ctx","tenantId","res","status","fileIdRaw","req","params","fileObjectId","fsDb","nativeDb","db","bucketName","userId","uploadKeyHash","reference","referenceMetadata","metadataUserId","ownerKeyHash","accessScope","authorizedByUser","Boolean","authorizedByKey","authorizedByTenant","referenceId","file","find","_id","limit","toArray","metadata","rawMetadata","fileMetadata","deduplication","variants","toHexString","variantIds","map","variant","filter","id","variantId","ApiHandler","getTenantFilesystemDb","GridFSBucket","ObjectId","findUploadFileReference","getBucketName","getTenantId","getUploadKeyHash","getUserId","resolveStoredUploadAccessScope","SessionUser","resolveHeaderString","value","normalized","trim","escapeHeaderFilename","filename","replace","parseRequestedWidth","parsed","Number","isFinite","Math","floor","resolveImageVariants","file","Record","metadata","image","variants","resolveVariantFileId","variant","fileId","selectVariantFileId","requestedWidth","requestedVariant","aliasWidth","toLowerCase","variantFileId","String","targetWidth","widths","Object","keys","map","width","filter","isInteger","sort","a","b","selectedWidth","reduce","selected","selectedDistance","abs","distance","getFile","_payload","ctx","Promise","tenantId","res","status","end","fileIdRaw","req","params","fileObjectId","fsDb","nativeDb","db","bucketName","bucket","reference","blobObjectId","blobId","find","_id","limit","toArray","rawMetadata","fileMetadata","accessScope","userId","uploadKeyHash","metadataUserId","ownerKeyHash","authorizedByUser","Boolean","authorizedByKey","query","w","variantFileObjectId","variantFile","selectedFileObjectId","selectedFile","mimeTypeFromMetadata","mimeType","filenameFromDb","filenameSafe","cacheControl","md5","uploadDate","Date","etagValue","toHexString","length","getTime","etag","ifNoneMatch","headers","candidates","split","includes","setHeader","flushHeaders","method","stream","openDownloadStream","on","destroy","pipe","ROUTE"],"sources":["../src/uploads/api/files/handlers/deleteFile.ts","../src/uploads/api/files/handlers/getFile.ts","../src/uploads/api/files/index.ts","../src/uploads/api/files/handler.ts"],"sourcesContent":["import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\n\nimport {\n deleteUploadFileReference,\n findUploadFileReference,\n} from \"../../file-uploads/fileReferences\"\nimport {\n getBucketName,\n getTenantId,\n getUploadKeyHash,\n getUserId,\n resolveStoredUploadAccessScope,\n type SessionUser,\n} from \"../../file-uploads/shared\"\n\n\ntype DeleteResponsePayload = {\n ok: boolean\n error?: string\n}\n\nconst resolveHeaderString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst isFileNotFoundError = (error: unknown): boolean => {\n const message = error instanceof Error ? error.message : String(error)\n return message.includes(\"FileNotFound\")\n}\n\nconst deleteGridFsFile = async (bucket: GridFSBucket, fileId: ObjectId): Promise<void> => {\n try {\n await bucket.delete(fileId)\n } catch (error) {\n if (!isFileNotFoundError(error)) throw error\n }\n}\n\nexport const deleteFile: ApiHandler<Record<string, never>, DeleteResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<DeleteResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const fileIdRaw = String(ctx.req.params?.fileId ?? \"\").trim()\n let fileObjectId: ObjectId\n try {\n fileObjectId = new ObjectId(fileIdRaw)\n } catch {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_file_id\" }\n }\n\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 const bucket = new GridFSBucket(nativeDb, { bucketName })\n\n const userId = getUserId(ctx)\n const uploadKeyHash = getUploadKeyHash(ctx)\n if (!userId && !uploadKeyHash) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId)\n if (reference) {\n const referenceMetadata = reference as unknown as Record<string, unknown>\n const metadataUserId = resolveHeaderString(reference.userId)\n const ownerKeyHash = resolveHeaderString(reference.ownerKeyHash)\n const accessScope = resolveStoredUploadAccessScope(referenceMetadata)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n const authorizedByTenant = accessScope === \"tenant\" && Boolean(userId)\n\n if (!authorizedByUser && !authorizedByKey && !authorizedByTenant) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n try {\n await deleteUploadFileReference({\n tenantId,\n bucketName,\n referenceId: fileObjectId,\n })\n } catch {\n ctx.res.status(500)\n return { ok: false, error: \"delete_failed\" }\n }\n\n ctx.res.status(204)\n return { ok: true }\n }\n\n const [file] = await bucket.find({ _id: fileObjectId }).limit(1).toArray()\n if (!file) {\n ctx.res.status(204)\n return { ok: true }\n }\n\n const metadataUserId = resolveHeaderString((file as any)?.metadata?.userId)\n const ownerKeyHash = resolveHeaderString((file as any)?.metadata?.ownerKeyHash)\n const rawMetadata = (file as any)?.metadata\n const fileMetadata = rawMetadata && typeof rawMetadata === \"object\"\n ? rawMetadata as Record<string, unknown>\n : {}\n const accessScope = resolveStoredUploadAccessScope(fileMetadata)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n const authorizedByTenant = accessScope === \"tenant\" && Boolean(userId)\n\n if (!authorizedByUser && !authorizedByKey && !authorizedByTenant) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n if (fileMetadata.deduplication && typeof fileMetadata.deduplication === \"object\") {\n ctx.res.status(409)\n return { ok: false, error: \"file_reference_required\" }\n }\n\n try {\n const variants = await bucket.find({ \"metadata.variantOf\": fileObjectId.toHexString() }).toArray()\n const variantIds = variants\n .map((variant) => (variant as any)?._id)\n .filter((id): id is ObjectId => id instanceof ObjectId)\n\n await deleteGridFsFile(bucket, fileObjectId)\n for (const variantId of variantIds) {\n await deleteGridFsFile(bucket, variantId)\n }\n } catch {\n ctx.res.status(500)\n return { ok: false, error: \"delete_failed\" }\n }\n\n ctx.res.status(204)\n return { ok: true }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\n\nimport { findUploadFileReference } from \"../../file-uploads/fileReferences\"\nimport {\n getBucketName,\n getTenantId,\n getUploadKeyHash,\n getUserId,\n resolveStoredUploadAccessScope,\n type SessionUser,\n} from \"../../file-uploads/shared\"\n\n\nconst resolveHeaderString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst escapeHeaderFilename = (filename: string): string => filename.replace(/[\\\\\"]/g, \"_\")\n\nconst parseRequestedWidth = (value: unknown): number | null => {\n if (typeof value !== \"string\") return null\n const parsed = Number(value.trim())\n if (!Number.isFinite(parsed) || parsed <= 0) return null\n return Math.floor(parsed)\n}\n\nconst resolveImageVariants = (file: unknown): Record<string, unknown> | null => {\n const metadata = (file as any)?.metadata\n if (!metadata || typeof metadata !== \"object\") return null\n const image = (metadata as Record<string, unknown>).image\n if (!image || typeof image !== \"object\") return null\n const variants = (image as Record<string, unknown>).variants\n if (!variants || typeof variants !== \"object\") return null\n return variants as Record<string, unknown>\n}\n\nconst resolveVariantFileId = (variant: unknown): ObjectId | null => {\n if (!variant || typeof variant !== \"object\") return null\n const fileId = (variant as Record<string, unknown>).fileId\n if (typeof fileId !== \"string\") return null\n try {\n return new ObjectId(fileId)\n } catch {\n return null\n }\n}\n\nconst selectVariantFileId = (file: unknown, requestedWidth: number | null, requestedVariant: unknown): ObjectId | null => {\n const variants = resolveImageVariants(file)\n if (!variants) return null\n\n let aliasWidth: number | null = null\n if (typeof requestedVariant === \"string\") {\n const normalized = requestedVariant.trim().toLowerCase()\n aliasWidth = normalized === \"thumb\" ? 320 : normalized === \"small\" ? 640 : normalized === \"medium\" ? 960 : normalized === \"large\" ? 1600 : null\n if (aliasWidth) {\n const variantFileId = resolveVariantFileId(variants[String(aliasWidth)])\n if (variantFileId) return variantFileId\n }\n }\n\n const targetWidth = aliasWidth ?? requestedWidth\n if (!targetWidth) return null\n\n const widths = Object.keys(variants)\n .map((width) => Number(width))\n .filter((width) => Number.isInteger(width) && width > 0)\n .sort((a, b) => a - b)\n\n const selectedWidth = widths.reduce<number | null>((selected, width) => {\n if (selected === null) return width\n\n const selectedDistance = Math.abs(selected - targetWidth)\n const distance = Math.abs(width - targetWidth)\n if (distance < selectedDistance) return width\n if (distance === selectedDistance && width > selected) return width\n return selected\n }, null)\n return selectedWidth ? resolveVariantFileId(variants[String(selectedWidth)]) : null\n}\n\nexport const getFile: ApiHandler<Record<string, never>, Record<string, never>, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Record<string, never>> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400).end()\n return {}\n }\n\n const fileIdRaw = String(ctx.req.params?.fileId ?? \"\").trim()\n let fileObjectId: ObjectId\n try {\n fileObjectId = new ObjectId(fileIdRaw)\n } catch {\n ctx.res.status(400).end()\n return {}\n }\n\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n ctx.res.status(500).end()\n return {}\n }\n\n const bucketName = getBucketName()\n const bucket = new GridFSBucket(nativeDb, { bucketName })\n const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId)\n const blobObjectId = reference?.blobId ?? fileObjectId\n\n const [file] = await bucket.find({ _id: blobObjectId }).limit(1).toArray()\n if (!file) {\n ctx.res.status(404).end()\n return {}\n }\n\n const rawMetadata = reference ?? (file as any)?.metadata\n const fileMetadata = rawMetadata && typeof rawMetadata === \"object\"\n ? rawMetadata as Record<string, unknown>\n : {}\n const accessScope = resolveStoredUploadAccessScope(fileMetadata)\n if (accessScope !== \"public\") {\n const userId = getUserId(ctx)\n if (accessScope === \"tenant\") {\n if (!userId) {\n ctx.res.status(401).end()\n return {}\n }\n } else {\n const uploadKeyHash = getUploadKeyHash(ctx)\n const metadataUserId = resolveHeaderString(fileMetadata.userId)\n const ownerKeyHash = resolveHeaderString(fileMetadata.ownerKeyHash)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n\n if (!authorizedByUser && !authorizedByKey) {\n ctx.res.status(401).end()\n return {}\n }\n }\n }\n\n const requestedWidth = parseRequestedWidth((ctx.req.query as any)?.w)\n const variantFileObjectId = selectVariantFileId(file, requestedWidth, (ctx.req.query as any)?.variant)\n const [variantFile] = variantFileObjectId\n ? await bucket.find({ _id: variantFileObjectId }).limit(1).toArray()\n : []\n const selectedFileObjectId = variantFile ? variantFileObjectId as ObjectId : blobObjectId\n const selectedFile = variantFile ?? file\n\n const mimeTypeFromMetadata = resolveHeaderString((selectedFile as any)?.metadata?.mimeType)\n const mimeType = mimeTypeFromMetadata ?? \"application/octet-stream\"\n const filenameFromDb = resolveHeaderString(reference?.filename ?? (selectedFile as any)?.filename)\n const filename = filenameFromDb ?? fileIdRaw\n const filenameSafe = escapeHeaderFilename(filename)\n\n const cacheControl = \"private, max-age=0, must-revalidate\"\n const md5 = resolveHeaderString((selectedFile as any)?.md5)\n const uploadDate = (selectedFile as any)?.uploadDate instanceof Date ? (selectedFile as any).uploadDate : null\n const etagValue = md5 ?? `${selectedFileObjectId.toHexString()}-${String((selectedFile as any)?.length ?? 0)}-${String(uploadDate?.getTime() ?? 0)}`\n const etag = md5 ? `\"${etagValue}\"` : `W/\"${etagValue}\"`\n const ifNoneMatch = resolveHeaderString((ctx.req.headers as any)?.[\"if-none-match\"])\n if (ifNoneMatch) {\n const candidates = ifNoneMatch.split(\",\").map((value) => value.trim()).filter(Boolean)\n if (candidates.includes(\"*\") || candidates.includes(etag)) {\n ctx.res.status(304)\n ctx.res.setHeader(\"Cache-Control\", cacheControl)\n ctx.res.setHeader(\"ETag\", etag)\n ctx.res.end()\n return {}\n }\n }\n\n ctx.res.status(200)\n ctx.res.setHeader(\"Content-Type\", mimeType)\n ctx.res.setHeader(\"Content-Length\", String((selectedFile as any)?.length ?? 0))\n ctx.res.setHeader(\"Content-Disposition\", `inline; filename=\"${filenameSafe}\"`)\n ctx.res.setHeader(\"Cache-Control\", cacheControl)\n ctx.res.setHeader(\"ETag\", etag)\n ctx.res.setHeader(\"X-Content-Type-Options\", \"nosniff\")\n if (mimeType === \"image/svg+xml\") {\n ctx.res.setHeader(\n \"Content-Security-Policy\",\n \"default-src 'none'; style-src 'unsafe-inline'; sandbox; base-uri 'none'; form-action 'none'\",\n )\n }\n ctx.res.flushHeaders()\n\n if (ctx.req.method === \"HEAD\") {\n ctx.res.end()\n return {}\n }\n\n const stream = bucket.openDownloadStream(selectedFileObjectId)\n\n stream.on(\"error\", () => {\n try {\n ctx.res.destroy()\n } catch {\n //\n }\n })\n\n stream.pipe(ctx.res)\n return {}\n}\n","export const ROUTE = \"/api/rb/files/:fileId\"\n","import { Api } from \"@rpcbase/api\"\n\nimport { deleteFile } from \"./handlers/deleteFile\"\nimport { getFile } from \"./handlers/getFile\"\n\nimport * as Files from \"./index\"\n\n\nexport default (api: Api) => {\n api.get(Files.ROUTE, getFile)\n api.delete(Files.ROUTE, deleteFile)\n}\n"],"mappings":";;;;;AAuBA,IAAMe,yBAAuBC,UAAkC;CAC7D,IAAI,OAAOA,UAAU,UAAU,OAAO;CACtC,MAAMC,aAAaD,MAAME,KAAK;CAC9B,OAAOD,aAAaA,aAAa;AACnC;AAEA,IAAME,uBAAuBL,UAA4B;CAEvD,QADgBA,iBAAiBO,QAAQP,MAAMM,UAAUE,OAAOR,KAAK,EAAA,CACtDS,SAAS,cAAc;AACxC;AAEA,IAAMC,mBAAmB,OAAOC,QAAsBC,WAAoC;CACxF,IAAI;EACF,MAAMD,OAAOG,OAAOF,MAAM;CAC5B,SAASZ,OAAO;EACd,IAAI,CAACK,oBAAoBL,KAAK,GAAG,MAAMA;CACzC;AACF;AAEA,IAAae,aAAoF,OAC/FE,UACAC,QACmC;CACnC,MAAMC,WAAW1B,YAAYyB,GAAG;CAChC,IAAI,CAACC,UAAU;EACbD,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAAiB;CAC9C;CAEA,MAAMsB,YAAYd,OAAOU,IAAIK,IAAIC,QAAQZ,UAAU,EAAE,CAAC,CAACR,KAAK;CAC5D,IAAIqB;CACJ,IAAI;EACFA,eAAe,IAAIpC,SAASiC,SAAS;CACvC,QAAQ;EACNJ,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAAkB;CAC/C;CAGA,MAAM2B,YAAWD,MADEvC,sBAAsBgC,QAAQ,EAAA,CAC3BS;CACtB,IAAI,CAACD,UAAU;EACbT,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAA4B;CACzD;CAEA,MAAM6B,aAAarC,cAAc;CACjC,MAAMmB,SAAS,IAAIvB,aAAauC,UAAU,EAAEE,WAAW,CAAC;CAExD,MAAMC,SAASnC,UAAUuB,GAAG;CAC5B,MAAMa,gBAAgBrC,iBAAiBwB,GAAG;CAC1C,IAAI,CAACY,UAAU,CAACC,eAAe;EAC7Bb,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAAe;CAC5C;CAEA,MAAMgC,YAAY,MAAMzC,wBAAwBoC,UAAUE,YAAYJ,YAAY;CAClF,IAAIO,WAAW;EACb,MAAMC,oBAAoBD;EAC1B,MAAME,iBAAiBjC,sBAAoB+B,UAAUF,MAAM;EAC3D,MAAMK,eAAelC,sBAAoB+B,UAAUG,YAAY;EAC/D,MAAMC,cAAcxC,+BAA+BqC,iBAAiB;EAMpE,IAAI,CAJqBK,QAAQR,UAAUI,kBAAkBJ,WAAWI,cAInEG,KAAoB,CAHDC,QAAQP,iBAAiBI,gBAAgBJ,kBAAkBI,YAGzDI,KAAmB,EAFlBH,gBAAgB,YAAYE,QAAQR,MAAM,IAEH;GAChEZ,IAAIE,IAAIC,OAAO,GAAG;GAClB,OAAO;IAAEtB,IAAI;IAAOC,OAAO;GAAe;EAC5C;EAEA,IAAI;GACF,MAAMV,0BAA0B;IAC9B6B;IACAU;IACAY,aAAahB;GACf,CAAC;EACH,QAAQ;GACNP,IAAIE,IAAIC,OAAO,GAAG;GAClB,OAAO;IAAEtB,IAAI;IAAOC,OAAO;GAAgB;EAC7C;EAEAkB,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO,EAAEtB,IAAI,KAAK;CACpB;CAEA,MAAM,CAAC2C,QAAQ,MAAM/B,OAAOgC,KAAK,EAAEC,KAAKnB,aAAa,CAAC,CAAC,CAACoB,MAAM,CAAC,CAAC,CAACC,QAAQ;CACzE,IAAI,CAACJ,MAAM;EACTxB,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO,EAAEtB,IAAI,KAAK;CACpB;CAEA,MAAMmC,iBAAiBjC,sBAAqByC,MAAcK,UAAUjB,MAAM;CAC1E,MAAMK,eAAelC,sBAAqByC,MAAcK,UAAUZ,YAAY;CAC9E,MAAMa,cAAeN,MAAcK;CACnC,MAAME,eAAeD,eAAe,OAAOA,gBAAgB,WACvDA,cACA,CAAC;CACL,MAAMZ,cAAcxC,+BAA+BqD,YAAY;CAM/D,IAAI,CAJqBX,QAAQR,UAAUI,kBAAkBJ,WAAWI,cAInEG,KAAoB,CAHDC,QAAQP,iBAAiBI,gBAAgBJ,kBAAkBI,YAGzDI,KAAmB,EAFlBH,gBAAgB,YAAYE,QAAQR,MAAM,IAEH;EAChEZ,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAAe;CAC5C;CAEA,IAAIiD,aAAaC,iBAAiB,OAAOD,aAAaC,kBAAkB,UAAU;EAChFhC,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAA0B;CACvD;CAEA,IAAI;EAEF,MAAMqD,cAAaF,MADIxC,OAAOgC,KAAK,EAAE,sBAAsBlB,aAAa2B,YAAY,EAAE,CAAC,CAAC,CAACN,QAAQ,EAAA,CAE9FQ,KAAKC,YAAaA,SAAiBX,GAAG,CAAC,CACvCY,QAAQC,OAAuBA,cAAcpE,QAAQ;EAExD,MAAMqB,iBAAiBC,QAAQc,YAAY;EAC3C,KAAK,MAAMiC,aAAaL,YACtB,MAAM3C,iBAAiBC,QAAQ+C,SAAS;CAE5C,QAAQ;EACNxC,IAAIE,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEtB,IAAI;GAAOC,OAAO;EAAgB;CAC7C;CAEAkB,IAAIE,IAAIC,OAAO,GAAG;CAClB,OAAO,EAAEtB,IAAI,KAAK;AACpB;;;AC3IA,IAAMuE,uBAAuBC,UAAkC;CAC7D,IAAI,OAAOA,UAAU,UAAU,OAAO;CACtC,MAAMC,aAAaD,MAAME,KAAK;CAC9B,OAAOD,aAAaA,aAAa;AACnC;AAEA,IAAME,wBAAwBC,aAA6BA,SAASC,QAAQ,UAAU,GAAG;AAEzF,IAAMC,uBAAuBN,UAAkC;CAC7D,IAAI,OAAOA,UAAU,UAAU,OAAO;CACtC,MAAMO,SAASC,OAAOR,MAAME,KAAK,CAAC;CAClC,IAAI,CAACM,OAAOC,SAASF,MAAM,KAAKA,UAAU,GAAG,OAAO;CACpD,OAAOG,KAAKC,MAAMJ,MAAM;AAC1B;AAEA,IAAMK,wBAAwBC,SAAkD;CAC9E,MAAME,WAAYF,MAAcE;CAChC,IAAI,CAACA,YAAY,OAAOA,aAAa,UAAU,OAAO;CACtD,MAAMC,QAASD,SAAqCC;CACpD,IAAI,CAACA,SAAS,OAAOA,UAAU,UAAU,OAAO;CAChD,MAAMC,WAAYD,MAAkCC;CACpD,IAAI,CAACA,YAAY,OAAOA,aAAa,UAAU,OAAO;CACtD,OAAOA;AACT;AAEA,IAAMC,wBAAwBC,YAAsC;CAClE,IAAI,CAACA,WAAW,OAAOA,YAAY,UAAU,OAAO;CACpD,MAAMC,SAAUD,QAAoCC;CACpD,IAAI,OAAOA,WAAW,UAAU,OAAO;CACvC,IAAI;EACF,OAAO,IAAI7B,SAAS6B,MAAM;CAC5B,QAAQ;EACN,OAAO;CACT;AACF;AAEA,IAAMC,uBAAuBR,MAAeS,gBAA+BC,qBAA+C;CACxH,MAAMN,WAAWL,qBAAqBC,IAAI;CAC1C,IAAI,CAACI,UAAU,OAAO;CAEtB,IAAIO,aAA4B;CAChC,IAAI,OAAOD,qBAAqB,UAAU;EACxC,MAAMtB,aAAasB,iBAAiBrB,KAAK,CAAC,CAACuB,YAAY;EACvDD,aAAavB,eAAe,UAAU,MAAMA,eAAe,UAAU,MAAMA,eAAe,WAAW,MAAMA,eAAe,UAAU,OAAO;EAC3I,IAAIuB,YAAY;GACd,MAAME,gBAAgBR,qBAAqBD,SAASU,OAAOH,UAAU,EAAE;GACvE,IAAIE,eAAe,OAAOA;EAC5B;CACF;CAEA,MAAME,cAAcJ,cAAcF;CAClC,IAAI,CAACM,aAAa,OAAO;CAOzB,MAAMW,gBALST,OAAOC,KAAKd,QAAQ,CAAC,CACjCe,KAAKC,UAAUzB,OAAOyB,KAAK,CAAC,CAAC,CAC7BC,QAAQD,UAAUzB,OAAO2B,UAAUF,KAAK,KAAKA,QAAQ,CAAC,CAAC,CACvDG,MAAMC,GAAGC,MAAMD,IAAIC,CAEAT,CAAAA,CAAOW,QAAuBC,UAAUR,UAAU;EACtE,IAAIQ,aAAa,MAAM,OAAOR;EAE9B,MAAMS,mBAAmBhC,KAAKiC,IAAIF,WAAWb,WAAW;EACxD,MAAMgB,WAAWlC,KAAKiC,IAAIV,QAAQL,WAAW;EAC7C,IAAIgB,WAAWF,kBAAkB,OAAOT;EACxC,IAAIW,aAAaF,oBAAoBT,QAAQQ,UAAU,OAAOR;EAC9D,OAAOQ;CACT,GAAG,IAAI;CACP,OAAOF,gBAAgBrB,qBAAqBD,SAASU,OAAOY,aAAa,EAAE,IAAI;AACjF;AAEA,IAAaM,UAAiF,OAC5FC,UACAC,QACmC;CACnC,MAAME,WAAWvD,YAAYqD,GAAG;CAChC,IAAI,CAACE,UAAU;EACbF,IAAIG,IAAIC,OAAO,GAAG,CAAC,CAACC,IAAI;EACxB,OAAO,CAAC;CACV;CAEA,MAAMC,YAAY1B,OAAOoB,IAAIO,IAAIC,QAAQnC,UAAU,EAAE,CAAC,CAAClB,KAAK;CAC5D,IAAIsD;CACJ,IAAI;EACFA,eAAe,IAAIjE,SAAS8D,SAAS;CACvC,QAAQ;EACNN,IAAIG,IAAIC,OAAO,GAAG,CAAC,CAACC,IAAI;EACxB,OAAO,CAAC;CACV;CAGA,MAAMM,YAAWD,MADEpE,sBAAsB4D,QAAQ,EAAA,CAC3BU;CACtB,IAAI,CAACD,UAAU;EACbX,IAAIG,IAAIC,OAAO,GAAG,CAAC,CAACC,IAAI;EACxB,OAAO,CAAC;CACV;CAEA,MAAMQ,aAAanE,cAAc;CACjC,MAAMoE,SAAS,IAAIvE,aAAaoE,UAAU,EAAEE,WAAW,CAAC;CACxD,MAAME,YAAY,MAAMtE,wBAAwBkE,UAAUE,YAAYJ,YAAY;CAClF,MAAMO,eAAeD,WAAWE,UAAUR;CAE1C,MAAM,CAAC3C,QAAQ,MAAMgD,OAAOI,KAAK,EAAEC,KAAKH,aAAa,CAAC,CAAC,CAACI,MAAM,CAAC,CAAC,CAACC,QAAQ;CACzE,IAAI,CAACvD,MAAM;EACTkC,IAAIG,IAAIC,OAAO,GAAG,CAAC,CAACC,IAAI;EACxB,OAAO,CAAC;CACV;CAEA,MAAMiB,cAAcP,aAAcjD,MAAcE;CAChD,MAAMuD,eAAeD,eAAe,OAAOA,gBAAgB,WACvDA,cACA,CAAC;CACL,MAAME,cAAc1E,+BAA+ByE,YAAY;CAC/D,IAAIC,gBAAgB,UAAU;EAC5B,MAAMC,SAAS5E,UAAUmD,GAAG;EAC5B,IAAIwB,gBAAgB;OACd,CAACC,QAAQ;IACXzB,IAAIG,IAAIC,OAAO,GAAG,CAAC,CAACC,IAAI;IACxB,OAAO,CAAC;GACV;SACK;GACL,MAAMqB,gBAAgB9E,iBAAiBoD,GAAG;GAC1C,MAAM2B,iBAAiB3E,oBAAoBuE,aAAaE,MAAM;GAC9D,MAAMG,eAAe5E,oBAAoBuE,aAAaK,YAAY;GAKlE,IAAI,CAHqBE,QAAQL,UAAUE,kBAAkBF,WAAWE,cAGnEE,KAAoB,CAFDC,QAAQJ,iBAAiBE,gBAAgBF,kBAAkBE,YAEzDG,GAAiB;IACzC/B,IAAIG,IAAIC,OAAO,GAAG,CAAC,CAACC,IAAI;IACxB,OAAO,CAAC;GACV;EACF;CACF;CAGA,MAAM6B,sBAAsB5D,oBAAoBR,MADzBP,oBAAqByC,IAAIO,IAAIyB,OAAeC,CACb1D,GAAiByB,IAAIO,IAAIyB,OAAe5D,OAAO;CACrG,MAAM,CAAC+D,eAAeD,sBAClB,MAAMpB,OAAOI,KAAK,EAAEC,KAAKe,oBAAoB,CAAC,CAAC,CAACd,MAAM,CAAC,CAAC,CAACC,QAAQ,IACjE,CAAA;CACJ,MAAMe,uBAAuBD,cAAcD,sBAAkClB;CAC7E,MAAMqB,eAAeF,eAAerE;CAGpC,MAAMyE,WADuBvF,oBAAqBqF,cAAsBrE,UAAUuE,QACjED,KAAwB;CAGzC,MAAMG,eAAerF,qBAFEJ,oBAAoB+D,WAAW1D,YAAagF,cAAsBhF,QACxEmF,KAAkBlC,SACe;CAElD,MAAMoC,eAAe;CACrB,MAAMC,MAAM3F,oBAAqBqF,cAAsBM,GAAG;CAC1D,MAAMC,aAAcP,cAAsBO,sBAAsBC,OAAQR,aAAqBO,aAAa;CAC1G,MAAME,YAAYH,OAAO,GAAGP,qBAAqBW,YAAY,EAAC,GAAInE,OAAQyD,cAAsBW,UAAU,CAAC,EAAC,GAAIpE,OAAOgE,YAAYK,QAAQ,KAAK,CAAC;CACjJ,MAAMC,OAAOP,MAAM,IAAIG,UAAS,KAAM,MAAMA,UAAS;CACrD,MAAMK,cAAcnG,oBAAqBgD,IAAIO,IAAI6C,UAAkB,gBAAgB;CACnF,IAAID,aAAa;EACf,MAAME,aAAaF,YAAYG,MAAM,GAAG,CAAC,CAACrE,KAAKhC,UAAUA,MAAME,KAAK,CAAC,CAAC,CAACgC,OAAO2C,OAAO;EACrF,IAAIuB,WAAWE,SAAS,GAAG,KAAKF,WAAWE,SAASL,IAAI,GAAG;GACzDlD,IAAIG,IAAIC,OAAO,GAAG;GAClBJ,IAAIG,IAAIqD,UAAU,iBAAiBd,YAAY;GAC/C1C,IAAIG,IAAIqD,UAAU,QAAQN,IAAI;GAC9BlD,IAAIG,IAAIE,IAAI;GACZ,OAAO,CAAC;EACV;CACF;CAEAL,IAAIG,IAAIC,OAAO,GAAG;CAClBJ,IAAIG,IAAIqD,UAAU,gBAAgBjB,QAAQ;CAC1CvC,IAAIG,IAAIqD,UAAU,kBAAkB5E,OAAQyD,cAAsBW,UAAU,CAAC,CAAC;CAC9EhD,IAAIG,IAAIqD,UAAU,uBAAuB,qBAAqBf,aAAY,EAAG;CAC7EzC,IAAIG,IAAIqD,UAAU,iBAAiBd,YAAY;CAC/C1C,IAAIG,IAAIqD,UAAU,QAAQN,IAAI;CAC9BlD,IAAIG,IAAIqD,UAAU,0BAA0B,SAAS;CACrD,IAAIjB,aAAa,iBACfvC,IAAIG,IAAIqD,UACN,2BACA,6FACF;CAEFxD,IAAIG,IAAIsD,aAAa;CAErB,IAAIzD,IAAIO,IAAImD,WAAW,QAAQ;EAC7B1D,IAAIG,IAAIE,IAAI;EACZ,OAAO,CAAC;CACV;CAEA,MAAMsD,SAAS7C,OAAO8C,mBAAmBxB,oBAAoB;CAE7DuB,OAAOE,GAAG,eAAe;EACvB,IAAI;GACF7D,IAAIG,IAAI2D,QAAQ;EAClB,QAAQ,CACN;CAEJ,CAAC;CAEDH,OAAOI,KAAK/D,IAAIG,GAAG;CACnB,OAAO,CAAC;AACV;;;ACpNA,IAAa6D,QAAQ;;;ACQrB,IAAA,mBAAgB,QAAa;CAC3B,IAAI,IAAI,OAAa,OAAO;CAC5B,IAAI,OAAO,OAAa,UAAU;AACpC"}
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- export declare const Route = "/api/rb/rts/changes";
2
+ export declare const ROUTE = "/api/rb/rts/changes";
3
3
  export declare const requestSchema: z.ZodObject<{
4
4
  sinceSeq: z.ZodDefault<z.ZodNumber>;
5
5
  limit: z.ZodDefault<z.ZodNumber>;
package/dist/rts/index.js CHANGED
@@ -4,7 +4,7 @@ import { randomUUID } from "node:crypto";
4
4
  import { buildAbilityFromSession } from "@rpcbase/db/acl";
5
5
  import { WebSocketServer } from "ws";
6
6
  //#region src/rts/routes.ts
7
- var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-BUTqP40B.js") }) }).reduce((acc, [path, mod]) => {
7
+ var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-BhpoyxI4.js") }) }).reduce((acc, [path, mod]) => {
8
8
  acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
9
9
  return acc;
10
10
  }, {});
@@ -1,4 +1,2 @@
1
- export declare const Route = "/api/rb/files/:fileId";
2
- export declare const GetRoute = "/api/rb/files/:fileId";
3
- export declare const DeleteRoute = "/api/rb/files/:fileId";
1
+ export declare const ROUTE = "/api/rb/files/:fileId";
4
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/files/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,0BAA0B,CAAA;AAE5C,eAAO,MAAM,QAAQ,0BAAQ,CAAA;AAC7B,eAAO,MAAM,WAAW,0BAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/uploads/api/files/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,0BAA0B,CAAA"}
package/dist/uploads.js CHANGED
@@ -2,7 +2,7 @@ import { a as getUploadProcessors, c as registerUploadProcessor, d as unregister
2
2
  //#region src/uploads/routes.ts
3
3
  var routes = Object.entries({ .../* #__PURE__ */ Object.assign({
4
4
  "./api/file-uploads/handler.ts": () => import("./handler-CqSlSCdd.js"),
5
- "./api/files/handler.ts": () => import("./handler-LRLmxrsg.js")
5
+ "./api/files/handler.ts": () => import("./handler-C63qGUhq.js")
6
6
  }) }).reduce((acc, [path, mod]) => {
7
7
  acc[path.replace("./api/", "@rpcbase/server/uploads/api/")] = mod;
8
8
  return acc;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/server",
3
- "version": "0.605.0",
3
+ "version": "0.606.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler-LRLmxrsg.js","names":["ApiHandler","getTenantFilesystemDb","GridFSBucket","ObjectId","deleteUploadFileReference","findUploadFileReference","getBucketName","getTenantId","getUploadKeyHash","getUserId","resolveStoredUploadAccessScope","SessionUser","DeleteResponsePayload","ok","error","resolveHeaderString","value","normalized","trim","isFileNotFoundError","message","Error","String","includes","deleteGridFsFile","bucket","fileId","Promise","delete","deleteFile","Record","_payload","ctx","tenantId","res","status","fileIdRaw","req","params","fileObjectId","fsDb","nativeDb","db","bucketName","userId","uploadKeyHash","reference","referenceMetadata","metadataUserId","ownerKeyHash","accessScope","authorizedByUser","Boolean","authorizedByKey","authorizedByTenant","referenceId","file","find","_id","limit","toArray","metadata","rawMetadata","fileMetadata","deduplication","variants","toHexString","variantIds","map","variant","filter","id","variantId","ApiHandler","getTenantFilesystemDb","GridFSBucket","ObjectId","findUploadFileReference","getBucketName","getTenantId","getUploadKeyHash","getUserId","resolveStoredUploadAccessScope","SessionUser","resolveHeaderString","value","normalized","trim","escapeHeaderFilename","filename","replace","parseRequestedWidth","parsed","Number","isFinite","Math","floor","resolveImageVariants","file","Record","metadata","image","variants","resolveVariantFileId","variant","fileId","selectVariantFileId","requestedWidth","requestedVariant","aliasWidth","toLowerCase","variantFileId","String","targetWidth","widths","Object","keys","map","width","filter","isInteger","sort","a","b","selectedWidth","reduce","selected","selectedDistance","abs","distance","getFile","_payload","ctx","Promise","tenantId","res","status","end","fileIdRaw","req","params","fileObjectId","fsDb","nativeDb","db","bucketName","bucket","reference","blobObjectId","blobId","find","_id","limit","toArray","rawMetadata","fileMetadata","accessScope","userId","uploadKeyHash","metadataUserId","ownerKeyHash","authorizedByUser","Boolean","authorizedByKey","query","w","variantFileObjectId","variantFile","selectedFileObjectId","selectedFile","mimeTypeFromMetadata","mimeType","filenameFromDb","filenameSafe","cacheControl","md5","uploadDate","Date","etagValue","toHexString","length","getTime","etag","ifNoneMatch","headers","candidates","split","includes","setHeader","flushHeaders","method","stream","openDownloadStream","on","destroy","pipe","Route","GetRoute","DeleteRoute"],"sources":["../src/uploads/api/files/handlers/deleteFile.ts","../src/uploads/api/files/handlers/getFile.ts","../src/uploads/api/files/index.ts","../src/uploads/api/files/handler.ts"],"sourcesContent":["import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\n\nimport {\n deleteUploadFileReference,\n findUploadFileReference,\n} from \"../../file-uploads/fileReferences\"\nimport {\n getBucketName,\n getTenantId,\n getUploadKeyHash,\n getUserId,\n resolveStoredUploadAccessScope,\n type SessionUser,\n} from \"../../file-uploads/shared\"\n\n\ntype DeleteResponsePayload = {\n ok: boolean\n error?: string\n}\n\nconst resolveHeaderString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst isFileNotFoundError = (error: unknown): boolean => {\n const message = error instanceof Error ? error.message : String(error)\n return message.includes(\"FileNotFound\")\n}\n\nconst deleteGridFsFile = async (bucket: GridFSBucket, fileId: ObjectId): Promise<void> => {\n try {\n await bucket.delete(fileId)\n } catch (error) {\n if (!isFileNotFoundError(error)) throw error\n }\n}\n\nexport const deleteFile: ApiHandler<Record<string, never>, DeleteResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<DeleteResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const fileIdRaw = String(ctx.req.params?.fileId ?? \"\").trim()\n let fileObjectId: ObjectId\n try {\n fileObjectId = new ObjectId(fileIdRaw)\n } catch {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_file_id\" }\n }\n\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 const bucket = new GridFSBucket(nativeDb, { bucketName })\n\n const userId = getUserId(ctx)\n const uploadKeyHash = getUploadKeyHash(ctx)\n if (!userId && !uploadKeyHash) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId)\n if (reference) {\n const referenceMetadata = reference as unknown as Record<string, unknown>\n const metadataUserId = resolveHeaderString(reference.userId)\n const ownerKeyHash = resolveHeaderString(reference.ownerKeyHash)\n const accessScope = resolveStoredUploadAccessScope(referenceMetadata)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n const authorizedByTenant = accessScope === \"tenant\" && Boolean(userId)\n\n if (!authorizedByUser && !authorizedByKey && !authorizedByTenant) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n try {\n await deleteUploadFileReference({\n tenantId,\n bucketName,\n referenceId: fileObjectId,\n })\n } catch {\n ctx.res.status(500)\n return { ok: false, error: \"delete_failed\" }\n }\n\n ctx.res.status(204)\n return { ok: true }\n }\n\n const [file] = await bucket.find({ _id: fileObjectId }).limit(1).toArray()\n if (!file) {\n ctx.res.status(204)\n return { ok: true }\n }\n\n const metadataUserId = resolveHeaderString((file as any)?.metadata?.userId)\n const ownerKeyHash = resolveHeaderString((file as any)?.metadata?.ownerKeyHash)\n const rawMetadata = (file as any)?.metadata\n const fileMetadata = rawMetadata && typeof rawMetadata === \"object\"\n ? rawMetadata as Record<string, unknown>\n : {}\n const accessScope = resolveStoredUploadAccessScope(fileMetadata)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n const authorizedByTenant = accessScope === \"tenant\" && Boolean(userId)\n\n if (!authorizedByUser && !authorizedByKey && !authorizedByTenant) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n if (fileMetadata.deduplication && typeof fileMetadata.deduplication === \"object\") {\n ctx.res.status(409)\n return { ok: false, error: \"file_reference_required\" }\n }\n\n try {\n const variants = await bucket.find({ \"metadata.variantOf\": fileObjectId.toHexString() }).toArray()\n const variantIds = variants\n .map((variant) => (variant as any)?._id)\n .filter((id): id is ObjectId => id instanceof ObjectId)\n\n await deleteGridFsFile(bucket, fileObjectId)\n for (const variantId of variantIds) {\n await deleteGridFsFile(bucket, variantId)\n }\n } catch {\n ctx.res.status(500)\n return { ok: false, error: \"delete_failed\" }\n }\n\n ctx.res.status(204)\n return { ok: true }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\n\nimport { findUploadFileReference } from \"../../file-uploads/fileReferences\"\nimport {\n getBucketName,\n getTenantId,\n getUploadKeyHash,\n getUserId,\n resolveStoredUploadAccessScope,\n type SessionUser,\n} from \"../../file-uploads/shared\"\n\n\nconst resolveHeaderString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst escapeHeaderFilename = (filename: string): string => filename.replace(/[\\\\\"]/g, \"_\")\n\nconst parseRequestedWidth = (value: unknown): number | null => {\n if (typeof value !== \"string\") return null\n const parsed = Number(value.trim())\n if (!Number.isFinite(parsed) || parsed <= 0) return null\n return Math.floor(parsed)\n}\n\nconst resolveImageVariants = (file: unknown): Record<string, unknown> | null => {\n const metadata = (file as any)?.metadata\n if (!metadata || typeof metadata !== \"object\") return null\n const image = (metadata as Record<string, unknown>).image\n if (!image || typeof image !== \"object\") return null\n const variants = (image as Record<string, unknown>).variants\n if (!variants || typeof variants !== \"object\") return null\n return variants as Record<string, unknown>\n}\n\nconst resolveVariantFileId = (variant: unknown): ObjectId | null => {\n if (!variant || typeof variant !== \"object\") return null\n const fileId = (variant as Record<string, unknown>).fileId\n if (typeof fileId !== \"string\") return null\n try {\n return new ObjectId(fileId)\n } catch {\n return null\n }\n}\n\nconst selectVariantFileId = (file: unknown, requestedWidth: number | null, requestedVariant: unknown): ObjectId | null => {\n const variants = resolveImageVariants(file)\n if (!variants) return null\n\n let aliasWidth: number | null = null\n if (typeof requestedVariant === \"string\") {\n const normalized = requestedVariant.trim().toLowerCase()\n aliasWidth = normalized === \"thumb\" ? 320 : normalized === \"small\" ? 640 : normalized === \"medium\" ? 960 : normalized === \"large\" ? 1600 : null\n if (aliasWidth) {\n const variantFileId = resolveVariantFileId(variants[String(aliasWidth)])\n if (variantFileId) return variantFileId\n }\n }\n\n const targetWidth = aliasWidth ?? requestedWidth\n if (!targetWidth) return null\n\n const widths = Object.keys(variants)\n .map((width) => Number(width))\n .filter((width) => Number.isInteger(width) && width > 0)\n .sort((a, b) => a - b)\n\n const selectedWidth = widths.reduce<number | null>((selected, width) => {\n if (selected === null) return width\n\n const selectedDistance = Math.abs(selected - targetWidth)\n const distance = Math.abs(width - targetWidth)\n if (distance < selectedDistance) return width\n if (distance === selectedDistance && width > selected) return width\n return selected\n }, null)\n return selectedWidth ? resolveVariantFileId(variants[String(selectedWidth)]) : null\n}\n\nexport const getFile: ApiHandler<Record<string, never>, Record<string, never>, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Record<string, never>> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400).end()\n return {}\n }\n\n const fileIdRaw = String(ctx.req.params?.fileId ?? \"\").trim()\n let fileObjectId: ObjectId\n try {\n fileObjectId = new ObjectId(fileIdRaw)\n } catch {\n ctx.res.status(400).end()\n return {}\n }\n\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n ctx.res.status(500).end()\n return {}\n }\n\n const bucketName = getBucketName()\n const bucket = new GridFSBucket(nativeDb, { bucketName })\n const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId)\n const blobObjectId = reference?.blobId ?? fileObjectId\n\n const [file] = await bucket.find({ _id: blobObjectId }).limit(1).toArray()\n if (!file) {\n ctx.res.status(404).end()\n return {}\n }\n\n const rawMetadata = reference ?? (file as any)?.metadata\n const fileMetadata = rawMetadata && typeof rawMetadata === \"object\"\n ? rawMetadata as Record<string, unknown>\n : {}\n const accessScope = resolveStoredUploadAccessScope(fileMetadata)\n if (accessScope !== \"public\") {\n const userId = getUserId(ctx)\n if (accessScope === \"tenant\") {\n if (!userId) {\n ctx.res.status(401).end()\n return {}\n }\n } else {\n const uploadKeyHash = getUploadKeyHash(ctx)\n const metadataUserId = resolveHeaderString(fileMetadata.userId)\n const ownerKeyHash = resolveHeaderString(fileMetadata.ownerKeyHash)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n\n if (!authorizedByUser && !authorizedByKey) {\n ctx.res.status(401).end()\n return {}\n }\n }\n }\n\n const requestedWidth = parseRequestedWidth((ctx.req.query as any)?.w)\n const variantFileObjectId = selectVariantFileId(file, requestedWidth, (ctx.req.query as any)?.variant)\n const [variantFile] = variantFileObjectId\n ? await bucket.find({ _id: variantFileObjectId }).limit(1).toArray()\n : []\n const selectedFileObjectId = variantFile ? variantFileObjectId as ObjectId : blobObjectId\n const selectedFile = variantFile ?? file\n\n const mimeTypeFromMetadata = resolveHeaderString((selectedFile as any)?.metadata?.mimeType)\n const mimeType = mimeTypeFromMetadata ?? \"application/octet-stream\"\n const filenameFromDb = resolveHeaderString(reference?.filename ?? (selectedFile as any)?.filename)\n const filename = filenameFromDb ?? fileIdRaw\n const filenameSafe = escapeHeaderFilename(filename)\n\n const cacheControl = \"private, max-age=0, must-revalidate\"\n const md5 = resolveHeaderString((selectedFile as any)?.md5)\n const uploadDate = (selectedFile as any)?.uploadDate instanceof Date ? (selectedFile as any).uploadDate : null\n const etagValue = md5 ?? `${selectedFileObjectId.toHexString()}-${String((selectedFile as any)?.length ?? 0)}-${String(uploadDate?.getTime() ?? 0)}`\n const etag = md5 ? `\"${etagValue}\"` : `W/\"${etagValue}\"`\n const ifNoneMatch = resolveHeaderString((ctx.req.headers as any)?.[\"if-none-match\"])\n if (ifNoneMatch) {\n const candidates = ifNoneMatch.split(\",\").map((value) => value.trim()).filter(Boolean)\n if (candidates.includes(\"*\") || candidates.includes(etag)) {\n ctx.res.status(304)\n ctx.res.setHeader(\"Cache-Control\", cacheControl)\n ctx.res.setHeader(\"ETag\", etag)\n ctx.res.end()\n return {}\n }\n }\n\n ctx.res.status(200)\n ctx.res.setHeader(\"Content-Type\", mimeType)\n ctx.res.setHeader(\"Content-Length\", String((selectedFile as any)?.length ?? 0))\n ctx.res.setHeader(\"Content-Disposition\", `inline; filename=\"${filenameSafe}\"`)\n ctx.res.setHeader(\"Cache-Control\", cacheControl)\n ctx.res.setHeader(\"ETag\", etag)\n ctx.res.setHeader(\"X-Content-Type-Options\", \"nosniff\")\n if (mimeType === \"image/svg+xml\") {\n ctx.res.setHeader(\n \"Content-Security-Policy\",\n \"default-src 'none'; style-src 'unsafe-inline'; sandbox; base-uri 'none'; form-action 'none'\",\n )\n }\n ctx.res.flushHeaders()\n\n if (ctx.req.method === \"HEAD\") {\n ctx.res.end()\n return {}\n }\n\n const stream = bucket.openDownloadStream(selectedFileObjectId)\n\n stream.on(\"error\", () => {\n try {\n ctx.res.destroy()\n } catch {\n //\n }\n })\n\n stream.pipe(ctx.res)\n return {}\n}\n","export const Route = \"/api/rb/files/:fileId\"\n\nexport const GetRoute = Route\nexport const DeleteRoute = Route\n","import { Api } from \"@rpcbase/api\"\n\nimport { deleteFile } from \"./handlers/deleteFile\"\nimport { getFile } from \"./handlers/getFile\"\n\nimport * as Files from \"./index\"\n\n\nexport default (api: Api) => {\n api.get(Files.GetRoute, getFile)\n api.delete(Files.DeleteRoute, deleteFile)\n}\n"],"mappings":"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"}