feat(attachments): depolama soyutlaması, içerikten tür tespiti, sharp/HEIC dönüşümü ve yetkili dosya servisi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { model, models, Schema, type InferSchemaType, type Model } from "mongoose";
|
||||
import { ATTACHMENT_KINDS } from "@/shared/attachments";
|
||||
|
||||
/** Ek dosyanın bağlanabileceği kayıt türleri (ileride: alışveriş fişi, cari hareket…). */
|
||||
export const ATTACHMENT_OWNER_TYPES = ["transaction"] as const;
|
||||
export type AttachmentOwnerType = (typeof ATTACHMENT_OWNER_TYPES)[number];
|
||||
|
||||
/**
|
||||
* Ek dosya meta verisi (Bölüm 7.4). Dosyanın kendisi depolama sürücüsünde, rastgele anahtarla.
|
||||
* - Yüklenince `ownerId: null` (bekleyen); kayıt kaydedilince sahibine bağlanır.
|
||||
* - Bekleyen ek yalnızca yükleyene görünür ve 24 saat içinde bağlanmazsa temizlenir.
|
||||
* - Kayıttan çıkarılan ek `deletedAt` alır; dosya saklama süresi sonunda silinir.
|
||||
*/
|
||||
const attachmentSchema = new Schema(
|
||||
{
|
||||
householdId: { type: Schema.Types.ObjectId, required: true },
|
||||
ownerType: { type: String, enum: ATTACHMENT_OWNER_TYPES, default: null },
|
||||
ownerId: { type: Schema.Types.ObjectId, default: null },
|
||||
kind: { type: String, enum: ATTACHMENT_KINDS, required: true },
|
||||
/** Saklanan içeriğin türü (görseller her zaman WebP'ye çevrilir). */
|
||||
mimeType: { type: String, enum: ["image/webp", "application/pdf"], required: true },
|
||||
/** Kaynak tür (jpeg, png, webp, heic, pdf) — bilgi amaçlı. */
|
||||
sourceType: { type: String, required: true },
|
||||
originalName: { type: String, default: "", maxlength: 120 },
|
||||
size: { type: Number, required: true },
|
||||
originalSize: { type: Number, required: true },
|
||||
width: { type: Number, default: null },
|
||||
height: { type: Number, default: null },
|
||||
storageKey: { type: String, required: true },
|
||||
thumbKey: { type: String, default: null },
|
||||
uploadedBy: { type: Schema.Types.ObjectId, required: true },
|
||||
/** Kayıt içindeki sıra (carousel sırası). */
|
||||
order: { type: Number, default: 0 },
|
||||
deletedAt: { type: Date, default: null },
|
||||
},
|
||||
{ timestamps: true, collection: "attachments" },
|
||||
);
|
||||
|
||||
attachmentSchema.index({ householdId: 1, ownerType: 1, ownerId: 1, deletedAt: 1, order: 1 });
|
||||
attachmentSchema.index({ ownerId: 1, createdAt: 1 });
|
||||
attachmentSchema.index(
|
||||
{ deletedAt: 1 },
|
||||
{ partialFilterExpression: { deletedAt: { $type: "date" } } },
|
||||
);
|
||||
attachmentSchema.index({ storageKey: 1 }, { unique: true });
|
||||
|
||||
export type Attachment = InferSchemaType<typeof attachmentSchema>;
|
||||
|
||||
export const AttachmentModel: Model<Attachment> =
|
||||
(models.Attachment as Model<Attachment> | undefined) ??
|
||||
model<Attachment>("Attachment", attachmentSchema);
|
||||
Reference in New Issue
Block a user