import type { FileDavDoDb } from "@/worker/db/file-dav-do/client"; import { deleteLocks, ensureRoot, expiredLocks, expiredPendingUploads, getBlob, markPendingUpload, pendingUploadByBlob, rootNode, } from "@/worker/db/file-dav-do/repository"; import type { BlobGcCandidate, CleanupExpiredLocksInput, CleanupExpiredPendingUploadsInput, FileDavRepairInput, VerifyBlobUnreferencedInput, } from "@/worker/objects/file-dav/types"; function storedSubjectId(db: FileDavDoDb): string | null { return db.query.fileMeta.findFirst({ where: (meta, { eq }) => eq(meta.key, "subject_id") }).sync()?.value ?? null; } export function verifyBlobUnreferenced( db: FileDavDoDb, input: VerifyBlobUnreferencedInput, ): { ok: true; unreferenced: boolean } { const existingSubjectId = storedSubjectId(db); if (existingSubjectId && existingSubjectId !== input.subjectId) return { ok: true, unreferenced: false }; const pending = pendingUploadByBlob(db, input.blobId); if (pending?.state === "pending") return { ok: true, unreferenced: false }; const blob = getBlob(db, input.blobId); if (!blob) return { ok: true, unreferenced: true }; if (blob.blobKey !== input.blobKey) return { ok: true, unreferenced: false }; return { ok: true, unreferenced: blob.refcount <= 0 }; } export function cleanupExpiredPendingUploads( db: FileDavDoDb, input: CleanupExpiredPendingUploadsInput, ): { ok: true; cleaned: number; blobGc: BlobGcCandidate[] } { ensureRoot(db, input.subjectId, input.nowMs); const expired = expiredPendingUploads(db, input.nowMs, input.limit); for (const upload of expired) markPendingUpload(db, upload.uploadId, "aborted"); return { ok: true, cleaned: expired.length, blobGc: expired.map((upload) => ({ blobId: upload.blobId, blobKey: upload.blobKey })), }; } export function cleanupExpiredLocks(db: FileDavDoDb, input: CleanupExpiredLocksInput): { ok: true; cleaned: number } { ensureRoot(db, input.subjectId, input.nowMs); const expired = expiredLocks(db, input.nowMs, input.limit); deleteLocks( db, expired.map((lock) => lock.token), ); return { ok: true, cleaned: expired.length }; } export function repairFileDav(db: FileDavDoDb, input: FileDavRepairInput) { if (!rootNode(db)) ensureRoot(db, input.subjectId, input.nowMs); const pendingUploads = cleanupExpiredPendingUploads(db, { subjectId: input.subjectId, storageId: input.storageId, nowMs: input.nowMs, limit: input.limit, }); const locks = cleanupExpiredLocks(db, { subjectId: input.subjectId, storageId: input.storageId, nowMs: input.nowMs, limit: input.limit, }); return { ok: true as const, cleanedPendingUploads: pendingUploads.cleaned, cleanedLocks: locks.cleaned, blobGc: pendingUploads.blobGc, }; }