@datrix/api-upload 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts","../src/handler.ts","../src/processor.ts","../src/upload.ts","../src/providers/local.ts","../src/providers/s3.ts"],"sourcesContent":["/**\n * Media Schema Factory\n */\n\nimport { defineSchema } from \"@datrix/core\";\nimport type { SchemaPermission, SchemaDefinition } from \"@datrix/core\";\nimport type { UploadOptions } from \"./types\";\n\nexport function createMediaSchema(\n\toptions: UploadOptions,\n\tpermission?: SchemaPermission,\n): SchemaDefinition {\n\tconst modelName = options.modelName ?? \"media\";\n\n\treturn defineSchema({\n\t\tname: modelName,\n\t\tfields: {\n\t\t\tfilename: { type: \"string\", required: true },\n\t\t\toriginalName: { type: \"string\", required: true },\n\t\t\tmimeType: { type: \"string\", required: true },\n\t\t\tsize: { type: \"number\", required: true, integer: true },\n\t\t\tkey: { type: \"string\", required: true },\n\t\t\tvariants: { type: \"json\" },\n\t\t},\n\t\t...(permission !== undefined && { permission }),\n\t});\n}\n","/**\n * Upload Handler\n *\n * POST /upload — multipart/form-data parse, format conversion, variant generation, DB record\n * DELETE /upload/:id — provider delete (all variants) + DB record delete\n *\n * GET /upload and GET /upload/:id fall through to normal CRUD.\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type { UploadFile, MediaVariants } from \"@datrix/core\";\nimport type { DatrixEntry } from \"@datrix/core\";\nimport {\n\tDatrixApiError,\n\thandlerError,\n\tjsonResponse,\n\tdatrixErrorResponse,\n} from \"@datrix/api\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport type { UploadOptions } from \"./types\";\nimport { convertFormat, generateVariants, isImage } from \"./processor\";\n\nexport interface UploadHandlerOptions {\n\tdatrix: Datrix;\n\tuploadOptions: UploadOptions;\n\tinjectUrls?: (data: unknown) => Promise<unknown>;\n}\n\nexport async function handleUploadRequest(\n\trequest: Request,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\ttry {\n\t\tconst { method } = request;\n\t\tconst url = new URL(request.url);\n\n\t\tconst pathAfterUpload = url.pathname.replace(/.*\\/upload/, \"\");\n\t\tconst idSegment = pathAfterUpload.replace(/^\\//, \"\").split(\"/\")[0];\n\t\tconst id =\n\t\t\tidSegment !== undefined && idSegment !== \"\" ? Number(idSegment) : null;\n\n\t\tif (method === \"POST\" && id === null) {\n\t\t\treturn await handleUpload(request, options);\n\t\t}\n\n\t\tif (method === \"DELETE\" && id !== null) {\n\t\t\treturn await handleDeleteMedia(id, options);\n\t\t}\n\n\t\treturn datrixErrorResponse(handlerError.methodNotAllowed(method));\n\t} catch (error) {\n\t\tif (error instanceof DatrixValidationError || error instanceof DatrixError) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\tconst message = error instanceof Error ? error.message : \"Upload failed\";\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.internalError(\n\t\t\t\tmessage,\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t),\n\t\t);\n\t}\n}\n\nasync function handleUpload(\n\trequest: Request,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\tconst { datrix, uploadOptions } = options;\n\tconst modelName = uploadOptions.modelName ?? \"media\";\n\n\tconst contentType = request.headers.get(\"content-type\") ?? \"\";\n\tif (!contentType.includes(\"multipart/form-data\")) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\"Expected multipart/form-data\"),\n\t\t);\n\t}\n\n\tlet formData: FormData;\n\ttry {\n\t\tformData = await request.formData();\n\t} catch (error) {\n\t\tconst cause = error instanceof Error ? error : undefined;\n\t\tthrow new DatrixApiError(\"Failed to parse multipart form data\", {\n\t\t\tcode: \"MULTIPART_PARSE_ERROR\",\n\t\t\tstatus: 400,\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t}\n\n\tconst fileEntry = formData.get(\"file\");\n\tif (!(fileEntry instanceof File)) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\"No file field in form data\"),\n\t\t);\n\t}\n\n\tconst buffer = await fileEntry.arrayBuffer();\n\tconst rawFile: UploadFile = {\n\t\tfilename: fileEntry.name,\n\t\toriginalName: fileEntry.name,\n\t\tmimetype: fileEntry.type,\n\t\tsize: fileEntry.size,\n\t\tbuffer: new Uint8Array(buffer),\n\t};\n\n\tvalidateFileLimits(rawFile, uploadOptions);\n\n\t// Format conversion (if configured and file is an image)\n\tconst quality = uploadOptions.quality ?? 80;\n\tconst fileToUpload =\n\t\tuploadOptions.format !== undefined && isImage(rawFile.mimetype)\n\t\t\t? await convertFormat(rawFile, uploadOptions.format, quality)\n\t\t\t: rawFile;\n\n\tconst uploadFile: UploadFile = {\n\t\tfilename: fileToUpload.filename,\n\t\toriginalName: rawFile.originalName,\n\t\tmimetype: fileToUpload.mimetype,\n\t\tsize: fileToUpload.buffer.length,\n\t\tbuffer: fileToUpload.buffer,\n\t};\n\n\t// Upload original (or converted) file\n\tconst result = await uploadOptions.provider.upload(uploadFile);\n\n\t// Generate resolution variants (if configured and file is an image)\n\tlet variants: MediaVariants | null = null;\n\tif (uploadOptions.resolutions !== undefined && isImage(uploadFile.mimetype)) {\n\t\tconst generated = await generateVariants(\n\t\t\tuploadFile,\n\t\t\tuploadOptions.resolutions,\n\t\t\tuploadOptions.format,\n\t\t\tquality,\n\t\t\tasync (variantFile) => {\n\t\t\t\tconst variantResult = await uploadOptions.provider.upload(variantFile);\n\t\t\t\treturn { key: variantResult.key };\n\t\t\t},\n\t\t);\n\t\tvariants = generated;\n\t}\n\n\tconst mediaRecord = await datrix.raw.create(modelName, {\n\t\tfilename: result.key,\n\t\toriginalName: uploadFile.originalName,\n\t\tmimeType: uploadFile.mimetype,\n\t\tsize: uploadFile.size,\n\t\tkey: result.key,\n\t\t...(variants !== null && { variants }),\n\t});\n\n\tconst data = options.injectUrls\n\t\t? await options.injectUrls(mediaRecord)\n\t\t: mediaRecord;\n\n\treturn jsonResponse({ data }, 201);\n}\n\n/**\n * DELETE /upload/:id\n * Deletes all variant keys from storage, then the main record.\n */\nasync function handleDeleteMedia(\n\tid: number,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\tconst { datrix, uploadOptions } = options;\n\tconst modelName = uploadOptions.modelName ?? \"media\";\n\n\ttype MediaRecord = {\n\t\tkey: string;\n\t\tvariants: Record<string, { key: string }> | null;\n\t} & DatrixEntry;\n\tconst record = await datrix.raw.findById<MediaRecord>(modelName, id);\n\n\tif (record === null) {\n\t\treturn datrixErrorResponse(handlerError.recordNotFound(modelName, id));\n\t}\n\n\t// Delete variant files from storage\n\tif (record.variants !== null && record.variants !== undefined) {\n\t\tfor (const variant of Object.values(record.variants)) {\n\t\t\tawait uploadOptions.provider.delete(variant.key);\n\t\t}\n\t}\n\n\t// Delete main file from storage\n\tawait uploadOptions.provider.delete(record.key);\n\tawait datrix.raw.delete(modelName, id);\n\n\treturn jsonResponse({ data: { id } });\n}\n\nfunction validateFileLimits(file: UploadFile, options: UploadOptions): void {\n\tif (options.maxSize !== undefined && file.size > options.maxSize) {\n\t\tthrow new DatrixApiError(\n\t\t\t`File size ${file.size} exceeds maximum allowed size ${options.maxSize}`,\n\t\t\t{ code: \"FILE_TOO_LARGE\", status: 400 },\n\t\t);\n\t}\n\n\tif (\n\t\toptions.allowedMimeTypes !== undefined &&\n\t\toptions.allowedMimeTypes.length > 0 &&\n\t\t!isMimeTypeAllowed(file.mimetype, options.allowedMimeTypes)\n\t) {\n\t\tthrow new DatrixApiError(`MIME type ${file.mimetype} is not allowed`, {\n\t\t\tcode: \"INVALID_MIME_TYPE\",\n\t\t\tstatus: 400,\n\t\t});\n\t}\n}\n\nfunction isMimeTypeAllowed(\n\tmimetype: string,\n\tallowedTypes: readonly string[],\n): boolean {\n\tfor (const allowed of allowedTypes) {\n\t\tif (allowed === mimetype) return true;\n\t\tif (allowed.endsWith(\"/*\")) {\n\t\t\tconst prefix = allowed.slice(0, -2);\n\t\t\tif (mimetype.startsWith(prefix + \"/\")) return true;\n\t\t}\n\t}\n\treturn false;\n}\n","/**\n * Image Processor\n *\n * Handles format conversion and resolution variant generation using sharp.\n * Only processes images — non-image files are passed through unchanged.\n */\n\nimport type { UploadFile, MediaVariant, MediaVariants } from \"@datrix/core\";\nimport type { ImageFormat, ResolutionConfig } from \"./types\";\nimport { DatrixError } from \"@datrix/core\";\n\nconst IMAGE_MIME_TYPES = new Set([\n\t\"image/jpeg\",\n\t\"image/png\",\n\t\"image/webp\",\n\t\"image/avif\",\n\t\"image/gif\",\n\t\"image/tiff\",\n]);\n\nfunction isImage(mimetype: string): boolean {\n\treturn IMAGE_MIME_TYPES.has(mimetype);\n}\n\nfunction getMimeType(format: ImageFormat): string {\n\tconst map: Record<ImageFormat, string> = {\n\t\twebp: \"image/webp\",\n\t\tjpeg: \"image/jpeg\",\n\t\tpng: \"image/png\",\n\t\tavif: \"image/avif\",\n\t};\n\treturn map[format];\n}\n\nfunction getExtension(format: ImageFormat): string {\n\treturn format === \"jpeg\" ? \"jpg\" : format;\n}\n\n/**\n * Apply format conversion to a file buffer.\n * Returns the converted buffer and new mimetype.\n * If file is not an image or no format set, returns original unchanged.\n */\nexport async function convertFormat(\n\tfile: UploadFile,\n\tformat: ImageFormat,\n\tquality: number,\n): Promise<{ buffer: Uint8Array; mimetype: string; filename: string }> {\n\tif (!isImage(file.mimetype)) {\n\t\treturn {\n\t\t\tbuffer: file.buffer,\n\t\t\tmimetype: file.mimetype,\n\t\t\tfilename: file.filename,\n\t\t};\n\t}\n\n\tlet sharp: typeof import(\"sharp\");\n\ttry {\n\t\tsharp = (await import(\"sharp\"))\n\t\t\t.default as unknown as typeof import(\"sharp\");\n\t} catch (error) {\n\t\tthrow new DatrixError(\"sharp is not installed\", {\n\t\t\tcode: \"SHARP_NOT_FOUND\",\n\t\t\toperation: \"upload:convertFormat\",\n\t\t});\n\t}\n\n\ttry {\n\t\tconst converted = await sharp(file.buffer)\n\t\t\t.toFormat(format, { quality })\n\t\t\t.toBuffer();\n\n\t\tconst ext = getExtension(format);\n\t\tconst baseName = file.filename.replace(/\\.[^.]+$/, \"\");\n\t\tconst newFilename = `${baseName}.${ext}`;\n\n\t\treturn {\n\t\t\tbuffer: new Uint8Array(converted),\n\t\t\tmimetype: getMimeType(format),\n\t\t\tfilename: newFilename,\n\t\t};\n\t} catch (error) {\n\t\tthrow new DatrixError(\"Failed to convert image format\", {\n\t\t\tcode: \"IMAGE_CONVERT_ERROR\",\n\t\t\toperation: \"upload:convertFormat\",\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t});\n\t}\n}\n\n/**\n * Generate resolution variants for an image.\n * Returns a map of resolution name → MediaVariant.\n * Uploads each variant via the provider.\n */\nexport async function generateVariants<TResolutions extends string>(\n\tfile: UploadFile,\n\tresolutions: Record<TResolutions, ResolutionConfig>,\n\tformat: ImageFormat | undefined,\n\tquality: number,\n\tuploadFn: (variantFile: UploadFile) => Promise<{ key: string }>,\n): Promise<MediaVariants<TResolutions>> {\n\tif (!isImage(file.mimetype)) {\n\t\treturn {};\n\t}\n\n\tlet sharp: typeof import(\"sharp\");\n\ttry {\n\t\tsharp = (await import(\"sharp\"))\n\t\t\t.default as unknown as typeof import(\"sharp\");\n\t} catch (error) {\n\t\tthrow new DatrixError(\"sharp is not installed\", {\n\t\t\tcode: \"SHARP_NOT_FOUND\",\n\t\t\toperation: \"upload:generateVariants\",\n\t\t});\n\t}\n\n\tconst targetFormat = format ?? \"jpeg\";\n\tconst outputMime = getMimeType(targetFormat);\n\tconst outputExt = getExtension(targetFormat);\n\n\tconst variants: Partial<Record<TResolutions, MediaVariant>> = {};\n\n\tfor (const [name, config] of Object.entries(resolutions) as [\n\t\tTResolutions,\n\t\tResolutionConfig,\n\t][]) {\n\t\ttry {\n\t\t\tconst resizeOptions: import(\"sharp\").ResizeOptions = {\n\t\t\t\twidth: config.width,\n\t\t\t\t...(config.height !== undefined && { height: config.height }),\n\t\t\t\t...(config.fit !== undefined &&\n\t\t\t\t\tconfig.height !== undefined && { fit: config.fit }),\n\t\t\t};\n\n\t\t\tconst variantBuffer = await sharp(file.buffer)\n\t\t\t\t.resize(resizeOptions)\n\t\t\t\t.toFormat(targetFormat, { quality })\n\t\t\t\t.toBuffer({ resolveWithObject: true });\n\n\t\t\tconst baseName = file.filename.replace(/\\.[^.]+$/, \"\");\n\t\t\tconst variantFilename = `${baseName}-${name}.${outputExt}`;\n\n\t\t\tconst variantFile: UploadFile = {\n\t\t\t\tfilename: variantFilename,\n\t\t\t\toriginalName: variantFilename,\n\t\t\t\tmimetype: outputMime,\n\t\t\t\tsize: variantBuffer.data.length,\n\t\t\t\tbuffer: new Uint8Array(variantBuffer.data),\n\t\t\t};\n\n\t\t\tconst uploaded = await uploadFn(variantFile);\n\n\t\t\tvariants[name] = {\n\t\t\t\tkey: uploaded.key,\n\t\t\t\twidth: variantBuffer.info.width,\n\t\t\t\theight: variantBuffer.info.height,\n\t\t\t\tsize: variantBuffer.data.length,\n\t\t\t\tmimeType: outputMime,\n\t\t\t\turl: undefined!,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) throw error;\n\t\t\tthrow new DatrixError(`Failed to generate variant: ${name}`, {\n\t\t\t\tcode: \"VARIANT_GENERATE_ERROR\",\n\t\t\t\toperation: \"upload:generateVariants\",\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn variants as MediaVariants<TResolutions>;\n}\n\nexport { isImage };\n","/**\n * Upload — main class, implements IUpload interface.\n * Pass an instance to ApiPlugin: new ApiPlugin({ upload: new Upload({ ... }) })\n *\n * @template TResolutions - Union of resolution names (e.g. \"thumbnail\" | \"small\" | \"medium\")\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type { IUpload } from \"@datrix/core\";\nimport type { SchemaDefinition } from \"@datrix/core\";\nimport { createMediaSchema } from \"./schema\";\nimport { handleUploadRequest } from \"./handler\";\nimport type { UploadOptions } from \"./types\";\n\nexport class Upload<TResolutions extends string = string> implements IUpload {\n\tprivate readonly options: UploadOptions<TResolutions>;\n\n\treadonly provider: UploadOptions<TResolutions>[\"provider\"];\n\n\tconstructor(options: UploadOptions<TResolutions>) {\n\t\tthis.options = options;\n\t\tthis.provider = options.provider;\n\t}\n\n\tgetModelName(): string {\n\t\treturn this.options.modelName ?? \"media\";\n\t}\n\n\tgetSchemas(): SchemaDefinition[] {\n\t\tconst mediaSchema = createMediaSchema(\n\t\t\tthis.options,\n\t\t\tthis.options.permission,\n\t\t);\n\t\treturn [mediaSchema];\n\t}\n\n\tasync handleRequest(request: Request, datrix: Datrix): Promise<Response> {\n\t\treturn handleUploadRequest(request, {\n\t\t\tdatrix,\n\t\t\tuploadOptions: this.options,\n\t\t\tinjectUrls: (data) => this.injectUrls(data),\n\t\t});\n\t}\n\n\tasync injectUrls(data: unknown): Promise<unknown> {\n\t\treturn this.traverse(data);\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn this.options.provider.getUrl(key);\n\t}\n\n\tprivate async traverse(node: unknown): Promise<unknown> {\n\t\tif (Array.isArray(node)) {\n\t\t\tconst results: unknown[] = [];\n\t\t\tfor (const item of node) {\n\t\t\t\tresults.push(await this.traverse(item));\n\t\t\t}\n\t\t\treturn results;\n\t\t}\n\n\t\tif (node !== null && typeof node === \"object\") {\n\t\t\tconst obj = node as Record<string, unknown>;\n\t\t\tconst result: Record<string, unknown> = {};\n\n\t\t\tfor (const [k, v] of Object.entries(obj)) {\n\t\t\t\tresult[k] = await this.traverse(v);\n\t\t\t}\n\n\t\t\t// Inject url if this looks like a media object (has key, no url)\n\t\t\tif (typeof result[\"key\"] === \"string\" && result[\"url\"] === undefined) {\n\t\t\t\tresult[\"url\"] = this.options.provider.getUrl(result[\"key\"]);\n\t\t\t}\n\n\t\t\t// Inject urls into variants\n\t\t\tif (\n\t\t\t\tresult[\"variants\"] !== null &&\n\t\t\t\ttypeof result[\"variants\"] === \"object\"\n\t\t\t) {\n\t\t\t\tconst variants = result[\"variants\"] as Record<string, unknown>;\n\t\t\t\tfor (const [name, variant] of Object.entries(variants)) {\n\t\t\t\t\tif (variant !== null && typeof variant === \"object\") {\n\t\t\t\t\t\tconst v = variant as Record<string, unknown>;\n\t\t\t\t\t\tif (typeof v[\"key\"] === \"string\" && v[\"url\"] === undefined) {\n\t\t\t\t\t\t\tvariants[name] = {\n\t\t\t\t\t\t\t\t...v,\n\t\t\t\t\t\t\t\turl: this.options.provider.getUrl(v[\"key\"]),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\treturn node;\n\t}\n}\n","/**\n * Local Filesystem Storage Provider\n */\n\nimport type {\n\tStorageProvider,\n\tUploadFile,\n\tUploadResult,\n\tLocalProviderOptions,\n} from \"@datrix/core\";\nimport { generateUniqueFilename, sanitizeFilename } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\n\nclass UploadError extends DatrixError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, {\n\t\t\tcode: \"UPLOAD_ERROR\",\n\t\t\toperation: \"upload:local\",\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\nexport class LocalStorageProvider implements StorageProvider {\n\treadonly name = \"local\" as const;\n\n\tprivate readonly basePath: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly ensureDirectory: boolean;\n\n\tconstructor(options: LocalProviderOptions) {\n\t\tthis.basePath = options.basePath;\n\t\tthis.baseUrl = options.baseUrl;\n\t\tthis.ensureDirectory = options.ensureDirectory ?? true;\n\t}\n\n\tasync upload(file: UploadFile): Promise<UploadResult> {\n\t\ttry {\n\t\t\tconst fs = await import(\"fs/promises\");\n\t\t\tconst path = await import(\"path\");\n\n\t\t\tconst sanitized = sanitizeFilename(file.originalName);\n\t\t\tconst uniqueFilename = generateUniqueFilename(sanitized);\n\t\t\tconst fullPath = path.join(this.basePath, uniqueFilename);\n\n\t\t\tif (this.ensureDirectory) {\n\t\t\t\tconst dirPath = path.dirname(fullPath);\n\t\t\t\tawait fs.mkdir(dirPath, { recursive: true });\n\t\t\t}\n\n\t\t\tawait fs.writeFile(fullPath, file.buffer);\n\n\t\t\treturn {\n\t\t\t\tkey: uniqueFilename,\n\t\t\t\tsize: file.size,\n\t\t\t\tmimetype: file.mimetype,\n\t\t\t\tuploadedAt: new Date(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to upload file to local filesystem\", cause);\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\tconst fs = await import(\"fs/promises\");\n\t\tconst path = await import(\"path\");\n\n\t\tconst fullPath = path.join(this.basePath, key);\n\n\t\ttry {\n\t\t\tawait fs.access(fullPath);\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"File not found\", cause);\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.unlink(fullPath);\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\n\t\t\t\t\"Failed to delete file from local filesystem\",\n\t\t\t\tcause,\n\t\t\t);\n\t\t}\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn this.buildUrl(key);\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst fs = await import(\"fs/promises\");\n\t\t\tconst path = await import(\"path\");\n\t\t\tconst fullPath = path.join(this.basePath, key);\n\t\t\tawait fs.access(fullPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate buildUrl(key: string): string {\n\t\tconst cleanBaseUrl = this.baseUrl.replace(/\\/$/, \"\");\n\t\tconst cleanKey = key.replace(/^\\//, \"\");\n\t\treturn `${cleanBaseUrl}/${cleanKey}`;\n\t}\n}\n","/**\n * AWS S3 Storage Provider\n *\n * Implements AWS Signature V4 signing without external SDK.\n */\n\nimport type {\n\tStorageProvider,\n\tUploadFile,\n\tUploadResult,\n\tS3ProviderOptions,\n} from \"@datrix/core\";\nimport { generateUniqueFilename, sanitizeFilename } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\n\nclass UploadError extends DatrixError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, {\n\t\t\tcode: \"UPLOAD_ERROR\",\n\t\t\toperation: \"upload:s3\",\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\nexport class S3StorageProvider implements StorageProvider {\n\treadonly name = \"s3\" as const;\n\n\tprivate readonly bucket: string;\n\tprivate readonly region: string;\n\tprivate readonly accessKeyId: string;\n\tprivate readonly secretAccessKey: string;\n\tprivate readonly endpoint: string;\n\tprivate readonly pathPrefix: string;\n\n\tconstructor(options: S3ProviderOptions) {\n\t\tthis.bucket = options.bucket;\n\t\tthis.region = options.region;\n\t\tthis.accessKeyId = options.accessKeyId;\n\t\tthis.secretAccessKey = options.secretAccessKey;\n\t\tthis.endpoint = options.endpoint ?? `s3.${options.region}.amazonaws.com`;\n\t\tthis.pathPrefix = options.pathPrefix ?? \"uploads\";\n\t}\n\n\tasync upload(file: UploadFile): Promise<UploadResult> {\n\t\ttry {\n\t\t\tconst sanitized = sanitizeFilename(file.originalName);\n\t\t\tconst filename = generateUniqueFilename(sanitized);\n\t\t\tconst key = this.pathPrefix ? `${this.pathPrefix}/${filename}` : filename;\n\n\t\t\tawait this.putObject(key, file.buffer, file.mimetype);\n\n\t\t\treturn {\n\t\t\t\tkey,\n\t\t\t\tsize: file.size,\n\t\t\t\tmimetype: file.mimetype,\n\t\t\t\tuploadedAt: new Date(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof UploadError) throw error;\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to upload file to S3\", cause);\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.deleteObject(key);\n\t\t} catch (error) {\n\t\t\tif (error instanceof UploadError) throw error;\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to delete file from S3\", cause);\n\t\t}\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn `https://${this.bucket}.${this.endpoint}/${key}`;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.headObject(key);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async putObject(\n\t\tkey: string,\n\t\tbuffer: Uint8Array,\n\t\tcontentType: string,\n\t): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst host = `${this.bucket}.${this.endpoint}`;\n\t\tconst urlPath = `/${key}`;\n\t\tconst method = \"PUT\";\n\t\tconst date = new Date().toUTCString();\n\t\tconst contentHash = crypto\n\t\t\t.createHash(\"sha256\")\n\t\t\t.update(buffer)\n\t\t\t.digest(\"hex\");\n\t\tconst authorization = await this.signRequest(\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\thost,\n\t\t\tdate,\n\t\t\tcontentType,\n\t\t\tcontentHash,\n\t\t);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{\n\t\t\t\t\thostname: host,\n\t\t\t\t\tport: 443,\n\t\t\t\t\tpath: urlPath,\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tHost: host,\n\t\t\t\t\t\tDate: date,\n\t\t\t\t\t\t\"Content-Type\": contentType,\n\t\t\t\t\t\t\"Content-Length\": buffer.length,\n\t\t\t\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\t\t\t\tAuthorization: authorization,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlet body = \"\";\n\t\t\t\t\t\tres.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\t\tbody += chunk.toString();\n\t\t\t\t\t\t});\n\t\t\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\t\t\treject(new UploadError(`S3 upload failed: ${status} ${body}`));\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\treq.write(buffer);\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate async deleteObject(key: string): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst host = `${this.bucket}.${this.endpoint}`;\n\t\tconst urlPath = `/${key}`;\n\t\tconst method = \"DELETE\";\n\t\tconst date = new Date().toUTCString();\n\t\tconst contentHash = crypto.createHash(\"sha256\").update(\"\").digest(\"hex\");\n\t\tconst authorization = await this.signRequest(\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\thost,\n\t\t\tdate,\n\t\t\t\"\",\n\t\t\tcontentHash,\n\t\t);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{\n\t\t\t\t\thostname: host,\n\t\t\t\t\tport: 443,\n\t\t\t\t\tpath: urlPath,\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tHost: host,\n\t\t\t\t\t\tDate: date,\n\t\t\t\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\t\t\t\tAuthorization: authorization,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlet body = \"\";\n\t\t\t\t\t\tres.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\t\tbody += chunk.toString();\n\t\t\t\t\t\t});\n\t\t\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\t\t\treject(new UploadError(`S3 delete failed: ${status} ${body}`));\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate async headObject(key: string): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst host = `${this.bucket}.${this.endpoint}`;\n\t\tconst urlPath = `/${key}`;\n\t\tconst method = \"HEAD\";\n\t\tconst date = new Date().toUTCString();\n\t\tconst contentHash = crypto.createHash(\"sha256\").update(\"\").digest(\"hex\");\n\t\tconst authorization = await this.signRequest(\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\thost,\n\t\t\tdate,\n\t\t\t\"\",\n\t\t\tcontentHash,\n\t\t);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{\n\t\t\t\t\thostname: host,\n\t\t\t\t\tport: 443,\n\t\t\t\t\tpath: urlPath,\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tHost: host,\n\t\t\t\t\t\tDate: date,\n\t\t\t\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\t\t\t\tAuthorization: authorization,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) resolve();\n\t\t\t\t\telse reject(new UploadError(`Object not found: ${status}`));\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate async signRequest(\n\t\tmethod: string,\n\t\turlPath: string,\n\t\thost: string,\n\t\tdate: string,\n\t\t_contentType: string,\n\t\tcontentHash: string,\n\t): Promise<string> {\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst canonicalHeaders = `host:${host}\\nx-amz-content-sha256:${contentHash}\\nx-amz-date:${date}\\n`;\n\t\tconst signedHeaders = \"host;x-amz-content-sha256;x-amz-date\";\n\t\tconst canonicalRequest = [\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\t\"\",\n\t\t\tcanonicalHeaders,\n\t\t\tsignedHeaders,\n\t\t\tcontentHash,\n\t\t].join(\"\\n\");\n\n\t\tconst algorithm = \"AWS4-HMAC-SHA256\";\n\t\tconst amzDate = this.getAmzDate();\n\t\tconst credentialScope = `${this.getDateStamp()}/${this.region}/s3/aws4_request`;\n\t\tconst canonicalRequestHash = crypto\n\t\t\t.createHash(\"sha256\")\n\t\t\t.update(canonicalRequest)\n\t\t\t.digest(\"hex\");\n\t\tconst stringToSign = [\n\t\t\talgorithm,\n\t\t\tamzDate,\n\t\t\tcredentialScope,\n\t\t\tcanonicalRequestHash,\n\t\t].join(\"\\n\");\n\n\t\tconst signature = this.calculateSignature(crypto, stringToSign);\n\t\treturn `${algorithm} Credential=${this.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n\t}\n\n\tprivate calculateSignature(\n\t\tcrypto: typeof import(\"crypto\"),\n\t\tstringToSign: string,\n\t): string {\n\t\tconst kDate = crypto\n\t\t\t.createHmac(\"sha256\", `AWS4${this.secretAccessKey}`)\n\t\t\t.update(this.getDateStamp())\n\t\t\t.digest();\n\t\tconst kRegion = crypto\n\t\t\t.createHmac(\"sha256\", kDate)\n\t\t\t.update(this.region)\n\t\t\t.digest();\n\t\tconst kService = crypto.createHmac(\"sha256\", kRegion).update(\"s3\").digest();\n\t\tconst kSigning = crypto\n\t\t\t.createHmac(\"sha256\", kService)\n\t\t\t.update(\"aws4_request\")\n\t\t\t.digest();\n\t\treturn crypto\n\t\t\t.createHmac(\"sha256\", kSigning)\n\t\t\t.update(stringToSign)\n\t\t\t.digest(\"hex\");\n\t}\n\n\tprivate getAmzDate(): string {\n\t\treturn new Date().toISOString().replace(/[:-]|\\.\\d{3}/g, \"\");\n\t}\n\n\tprivate getDateStamp(): string {\n\t\tconst now = new Date();\n\t\tconst year = now.getUTCFullYear();\n\t\tconst month = String(now.getUTCMonth() + 1).padStart(2, \"0\");\n\t\tconst day = String(now.getUTCDate()).padStart(2, \"0\");\n\t\treturn `${year}${month}${day}`;\n\t}\n}\n"],"mappings":"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","names":["DatrixError","DatrixError","DatrixError","generateUniqueFilename","sanitizeFilename","DatrixError","UploadError"]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@datrix/api-upload",
3
+ "version": "0.1.0",
4
+ "description": "File upload extension for Datrix API plugin",
5
+ "author": "Datrix Contributors",
6
+ "license": "MIT",
7
+ "homepage": "https://datrix.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/myniqx/datrix.git",
11
+ "directory": "packages/api-upload"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/myniqx/datrix/issues"
15
+ },
16
+ "keywords": [
17
+ "upload",
18
+ "file",
19
+ "typescript",
20
+ "s3",
21
+ "image",
22
+ "datrix"
23
+ ],
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.mjs",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.mjs",
34
+ "require": "./dist/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "sideEffects": false,
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "peerDependencies": {
45
+ "@datrix/core": "^0.1.0",
46
+ "@datrix/api": "^0.1.0"
47
+ },
48
+ "dependencies": {
49
+ "sharp": "^0.34.5"
50
+ },
51
+ "devDependencies": {
52
+ "@types/sharp": "^0.32.0",
53
+ "tsup": "^8.5.1",
54
+ "typescript": "^5.3.3",
55
+ "@datrix/core": "0.1.0",
56
+ "@datrix/api": "0.1.0"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "type-check": "tsc --noEmit",
62
+ "clean": "rm -rf dist"
63
+ }
64
+ }