Browse Source

refactor(db): rename post_media_refs to media_refs

Made-with: Cursor
main
npmrun 6 hours ago
parent
commit
03ceb6a2bd
  1. 6
      packages/drizzle-pkg/database/sqlite/schema/content.ts
  2. BIN
      packages/drizzle-pkg/db.sqlite
  3. 2
      packages/drizzle-pkg/lib/schema/content.ts
  4. 3
      packages/drizzle-pkg/migrations/0004_rename_post_media_refs_to_media_refs.sql
  5. 1135
      packages/drizzle-pkg/migrations/meta/0004_snapshot.json
  6. 7
      packages/drizzle-pkg/migrations/meta/_journal.json
  7. 26
      server/service/media/index.ts
  8. 12
      server/service/media/storage-audit.ts
  9. 8
      server/service/posts/index.ts

6
packages/drizzle-pkg/database/sqlite/schema/content.ts

@ -58,8 +58,8 @@ export const mediaAssets = sqliteTable(
], ],
); );
export const postMediaRefs = sqliteTable( export const mediaRefs = sqliteTable(
"post_media_refs", "media_refs",
{ {
postId: integer("post_id") postId: integer("post_id")
.notNull() .notNull()
@ -70,7 +70,7 @@ export const postMediaRefs = sqliteTable(
}, },
(table) => [ (table) => [
primaryKey({ columns: [table.postId, table.assetId] }), primaryKey({ columns: [table.postId, table.assetId] }),
index("post_media_refs_asset_id_idx").on(table.assetId), index("media_refs_asset_id_idx").on(table.assetId),
], ],
); );

BIN
packages/drizzle-pkg/db.sqlite

Binary file not shown.

2
packages/drizzle-pkg/lib/schema/content.ts

@ -1 +1 @@
export { mediaAssets, postComments, postMediaRefs, posts, timelineEvents } from "../../database/sqlite/schema/content"; export { mediaAssets, mediaRefs, postComments, posts, timelineEvents } from "../../database/sqlite/schema/content";

3
packages/drizzle-pkg/migrations/0004_rename_post_media_refs_to_media_refs.sql

@ -0,0 +1,3 @@
ALTER TABLE `post_media_refs` RENAME TO `media_refs`;--> statement-breakpoint
DROP INDEX `post_media_refs_asset_id_idx`;--> statement-breakpoint
CREATE INDEX `media_refs_asset_id_idx` ON `media_refs` (`asset_id`);

1135
packages/drizzle-pkg/migrations/meta/0004_snapshot.json

File diff suppressed because it is too large

7
packages/drizzle-pkg/migrations/meta/_journal.json

@ -29,6 +29,13 @@
"when": 1776516043018, "when": 1776516043018,
"tag": "0003_media-assets", "tag": "0003_media-assets",
"breakpoints": true "breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1776600000000,
"tag": "0004_rename_post_media_refs_to_media_refs",
"breakpoints": true
} }
] ]
} }

26
server/service/media/index.ts

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

12
server/service/media/storage-audit.ts

@ -1,7 +1,7 @@
import fs from "node:fs"; import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import { dbGlobal } from "drizzle-pkg/lib/db"; 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 { count, eq } from "drizzle-orm"; import { count, eq } from "drizzle-orm";
import { RELATIVE_ASSETS_DIR } from "#server/constants/media"; import { RELATIVE_ASSETS_DIR } from "#server/constants/media";
@ -57,11 +57,11 @@ export async function auditMediaStorageVsDb(): Promise<StorageAuditResult> {
const refAgg = await dbGlobal const refAgg = await dbGlobal
.select({ .select({
assetId: postMediaRefs.assetId, assetId: mediaRefs.assetId,
c: count(), c: count(),
}) })
.from(postMediaRefs) .from(mediaRefs)
.groupBy(postMediaRefs.assetId); .groupBy(mediaRefs.assetId);
const refMap = new Map<number, number>(); const refMap = new Map<number, number>();
for (const r of refAgg) { for (const r of refAgg) {
@ -167,8 +167,8 @@ export async function removeUnreferencedDbRowsForMissingFiles(): Promise<{
} }
const [{ n }] = await dbGlobal const [{ n }] = await dbGlobal
.select({ n: count() }) .select({ n: count() })
.from(postMediaRefs) .from(mediaRefs)
.where(eq(postMediaRefs.assetId, c.id)); .where(eq(mediaRefs.assetId, c.id));
if (n > 0) { if (n > 0) {
continue; continue;
} }

8
server/service/posts/index.ts

@ -1,5 +1,5 @@
import { dbGlobal } from "drizzle-pkg/lib/db"; import { dbGlobal } from "drizzle-pkg/lib/db";
import { postMediaRefs, posts } from "drizzle-pkg/lib/schema/content"; import { mediaRefs, posts } from "drizzle-pkg/lib/schema/content";
import { reconcileAssetTimestampsAfterRefChange, syncPostMediaRefs } from "#server/service/media"; import { reconcileAssetTimestampsAfterRefChange, syncPostMediaRefs } from "#server/service/media";
import { users } from "drizzle-pkg/lib/schema/auth"; import { users } from "drizzle-pkg/lib/schema/auth";
import { and, count, desc, eq } from "drizzle-orm"; import { and, count, desc, eq } from "drizzle-orm";
@ -121,9 +121,9 @@ export async function deletePost(userId: number, id: number) {
return false; return false;
} }
const refRows = await dbGlobal const refRows = await dbGlobal
.select({ assetId: postMediaRefs.assetId }) .select({ assetId: mediaRefs.assetId })
.from(postMediaRefs) .from(mediaRefs)
.where(eq(postMediaRefs.postId, id)); .where(eq(mediaRefs.postId, id));
const touched = refRows.map((r) => r.assetId); const touched = refRows.map((r) => r.assetId);
await dbGlobal.delete(posts).where(and(eq(posts.id, id), eq(posts.userId, userId))); await dbGlobal.delete(posts).where(and(eq(posts.id, id), eq(posts.userId, userId)));
await reconcileAssetTimestampsAfterRefChange(touched); await reconcileAssetTimestampsAfterRefChange(touched);

Loading…
Cancel
Save