|
|
|
@ -1,7 +1,7 @@ |
|
|
|
import fs from "node:fs"; |
|
|
|
import path from "node:path"; |
|
|
|
import { dbGlobal } from "drizzle-pkg/lib/db"; |
|
|
|
import { mediaAssets, postMediaRefs } from "drizzle-pkg/lib/schema/content"; |
|
|
|
import { mediaAssets, mediaRefs } from "drizzle-pkg/lib/schema/content"; |
|
|
|
import { and, count, desc, eq, inArray, isNotNull, isNull, lte, not, notExists, or, sql } from "drizzle-orm"; |
|
|
|
import { |
|
|
|
MEDIA_ORPHAN_GRACE_HOURS_AFTER_DEREF, |
|
|
|
@ -104,7 +104,7 @@ export function computeOrphanGraceExpiresAt(row: { |
|
|
|
|
|
|
|
function orphanCondition() { |
|
|
|
return notExists( |
|
|
|
dbGlobal.select({ x: sql`1` }).from(postMediaRefs).where(eq(postMediaRefs.assetId, mediaAssets.id)), |
|
|
|
dbGlobal.select({ x: sql`1` }).from(mediaRefs).where(eq(mediaRefs.assetId, mediaAssets.id)), |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
@ -147,8 +147,8 @@ export async function reconcileAssetTimestampsAfterRefChange(assetIds: number[]) |
|
|
|
for (const id of unique) { |
|
|
|
const [{ c }] = await dbGlobal |
|
|
|
.select({ c: count() }) |
|
|
|
.from(postMediaRefs) |
|
|
|
.where(eq(postMediaRefs.assetId, id)); |
|
|
|
.from(mediaRefs) |
|
|
|
.where(eq(mediaRefs.assetId, id)); |
|
|
|
|
|
|
|
if (c > 0) { |
|
|
|
const [row] = await dbGlobal.select().from(mediaAssets).where(eq(mediaAssets.id, id)).limit(1); |
|
|
|
@ -189,12 +189,12 @@ export async function syncPostMediaRefs( |
|
|
|
coverUrl: string | null, |
|
|
|
): Promise<void> { |
|
|
|
const beforeRows = await dbGlobal |
|
|
|
.select({ assetId: postMediaRefs.assetId }) |
|
|
|
.from(postMediaRefs) |
|
|
|
.where(eq(postMediaRefs.postId, postId)); |
|
|
|
.select({ assetId: mediaRefs.assetId }) |
|
|
|
.from(mediaRefs) |
|
|
|
.where(eq(mediaRefs.postId, postId)); |
|
|
|
const beforeIds = beforeRows.map((r) => r.assetId); |
|
|
|
|
|
|
|
await dbGlobal.delete(postMediaRefs).where(eq(postMediaRefs.postId, postId)); |
|
|
|
await dbGlobal.delete(mediaRefs).where(eq(mediaRefs.postId, postId)); |
|
|
|
|
|
|
|
const urls = mergePostMediaUrls(bodyMarkdown, coverUrl); |
|
|
|
const keys = [...new Set(urls.map((u) => publicAssetUrlToStorageKey(u)).filter((k): k is string => k != null))]; |
|
|
|
@ -208,7 +208,7 @@ export async function syncPostMediaRefs( |
|
|
|
afterIds = assetRows.map((r) => r.id); |
|
|
|
if (afterIds.length > 0) { |
|
|
|
await dbGlobal |
|
|
|
.insert(postMediaRefs) |
|
|
|
.insert(mediaRefs) |
|
|
|
.values(afterIds.map((assetId) => ({ postId, assetId }))) |
|
|
|
.onConflictDoNothing(); |
|
|
|
} |
|
|
|
@ -285,8 +285,8 @@ export async function listOrphanCandidatesForUser( |
|
|
|
async function assertAssetDeletableOrThrow(row: typeof mediaAssets.$inferSelect): Promise<void> { |
|
|
|
const [{ c }] = await dbGlobal |
|
|
|
.select({ c: count() }) |
|
|
|
.from(postMediaRefs) |
|
|
|
.where(eq(postMediaRefs.assetId, row.id)); |
|
|
|
.from(mediaRefs) |
|
|
|
.where(eq(mediaRefs.assetId, row.id)); |
|
|
|
if (c > 0) { |
|
|
|
throw createError({ statusCode: 400, statusMessage: "资源仍被文章引用,无法删除" }); |
|
|
|
} |
|
|
|
@ -328,8 +328,8 @@ export async function purgeAllDeletableOrphansGlobally(limit: number): Promise<n |
|
|
|
for (const row of candidates) { |
|
|
|
const [{ c }] = await dbGlobal |
|
|
|
.select({ c: count() }) |
|
|
|
.from(postMediaRefs) |
|
|
|
.where(eq(postMediaRefs.assetId, row.id)); |
|
|
|
.from(mediaRefs) |
|
|
|
.where(eq(mediaRefs.assetId, row.id)); |
|
|
|
if (c > 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|