Add file module for uploads and storage
- File model + migration: file metadata, content lives in object storage - IFileStorage port with local disk and S3-compatible (RustFS/MinIO) adapters - POST /files, GET /files/:id/download, DELETE /files/:id - MIME sniffed from content; only safe images served inline, nosniff header - FileOwnerRegistry: owner modules register to grant visibility and keep their files from GC; FileGcService removes orphaned files hourly - TransformResponseInterceptor passes StreamableFile through Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5cfc8986d7
commit
f32c6e348d
49 files changed
+1992
-2
No files matched your search
@@ -0,0 +1,25 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "file" (
|
||||
"id" TEXT NOT NULL,
|
||||
"storageKey" TEXT NOT NULL,
|
||||
"fileName" TEXT NOT NULL,
|
||||
"mimeType" TEXT NOT NULL,
|
||||
"size" INTEGER NOT NULL,
|
||||
"uploaderId" TEXT,
|
||||
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "file_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "file_uploaderId_idx" ON "file"("uploaderId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "file_createdAt_idx" ON "file"("createdAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "file_storageKey_key" ON "file"("storageKey");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "file" ADD CONSTRAINT "file_uploaderId_fkey" FOREIGN KEY ("uploaderId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
@@ -25,6 +25,7 @@ model User {
|
||||
notificationPreferences NotificationPreference[]
|
||||
notificationMessages NotificationMessage[]
|
||||
comments Comment[]
|
||||
uploadedFiles File[]
|
||||
|
||||
@@unique([email])
|
||||
@@map("user")
|
||||
@@ -209,3 +210,23 @@ model Comment {
|
||||
@@index([authorId])
|
||||
@@map("comment")
|
||||
}
|
||||
|
||||
/// Загруженный файл. Ничего не знает о владельце: к задачам, комментариям и т.п.
|
||||
/// файлы привязываются через таблицы связей в модулях-владельцах.
|
||||
/// Файл, на который никто не ссылается, считается сиротой и удаляется GC.
|
||||
/// storageKey — ключ объекта в хранилище, имя от пользователя в него не попадает.
|
||||
model File {
|
||||
id String @id @default(uuid(7))
|
||||
storageKey String
|
||||
fileName String
|
||||
mimeType String
|
||||
size Int
|
||||
uploaderId String?
|
||||
uploader User? @relation(fields: [uploaderId], references: [id], onDelete: SetNull)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
||||
|
||||
@@unique([storageKey])
|
||||
@@index([uploaderId])
|
||||
@@index([createdAt])
|
||||
@@map("file")
|
||||
}
|
||||
Reference in new issue
Block a user