Files
2026-05-27 23:29:53 +01:00

3.1 KiB

OpenSpec Change: enterprise-api-plugin-file-upload

  • Change ID: enterprise-api-plugin-file-upload
  • Package: @suaveplan/api-plugin-file-upload
  • Category: api/data
  • Target path: api/data/api-plugin-file-upload
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/api-plugin-file-upload.md

Summary

Deliver @suaveplan/api-plugin-file-upload on the genesis branch from scratch at enterprise quality. File upload and management plugin: multipart upload (single + chunked), MIME validation via @suaveplan/mime, virus-scan hook, pluggable storage backend (StorageAdapter — local disk, S3-compatible via @suaveplan/storage-s3), metadata extraction (images via @suaveplan/exif, audio via @suaveplan/audio-metadata), signed download URL generation, image compression via @suaveplan/compress-image, and file lifecycle management (quarantine, approve, delete). Routes under /files. Per-table SurrealQL. Events: file:uploaded, file:approved, file:deleted.


Available workspace deps on genesis

Package Role
@suaveplan/api (workspace:^) definePlugin
@suaveplan/api-shared (workspace:^) Helpers
@suaveplan/api-plugin-auth (workspace:^) requireAuth
@suaveplan/api-plugin-database (workspace:^) SurrealDB
@suaveplan/mime (workspace:^) MIME type detection
@suaveplan/exif (workspace:^) Image EXIF extraction
@suaveplan/audio-metadata (workspace:^) Audio metadata extraction
@suaveplan/compress-image (workspace:^) Image compression
@suaveplan/events (workspace:^) Event bus
@suaveplan/error (workspace:^) FileUploadError hierarchy
@suaveplan/rate-limiter (workspace:^) Upload rate limits
hono + @hono/zod-openapi + zod Routes
@opentelemetry/api OTel

Public API surface

function createFileUploadPlugin(storageAdapter: FileStorageAdapter, config?: FileUploadPluginConfig): PluginDescriptor
interface FileStorageAdapter { put(key, stream, meta): Promise<string>; get(key): Promise<ReadableStream>; delete(key): Promise<void>; getSignedUrl(key, ttlSeconds): Promise<string> }
// Routes: POST /files, GET /files/:id, GET /files/:id/download, DELETE /files/:id, POST /files/chunked/initiate, POST /files/chunked/:uploadId/part, POST /files/chunked/:uploadId/complete

Error codes

type FileUploadErrorCode =
  | "FILE_NOT_FOUND"
  | "FILE_TYPE_NOT_ALLOWED"
  | "FILE_TOO_LARGE"
  | "FILE_QUARANTINED"
  | "FILE_UPLOAD_INCOMPLETE"
  | "FILE_STORAGE_ERROR"

File layout

api/data/api-plugin-file-upload/
  package.json  tsconfig.json  vitest.config.ts
  surql/ (file_record, chunked_upload tables)
  src/
    index.ts  events.ts  types/  errors/
    adapters/storage.port.ts
    services/upload/  services/metadata/  services/lifecycle/
    routes/files/  routes/chunked/
  tests/integration.test.ts

Impact

Prerequisites: @suaveplan/api, @suaveplan/api-plugin-auth, @suaveplan/api-plugin-database, @suaveplan/mime, @suaveplan/exif, @suaveplan/audio-metadata, @suaveplan/compress-image.