// 论坛内容 备份+清空 工具(在 app 容器内运行) // 用法: node _forum-clear.mjs dump | node _forum-clear.mjs clear const fs = require("fs"); const { PrismaMariaDb } = require("@prisma/adapter-mariadb"); const { PrismaClient } = require("@prisma/client"); const env = fs.readFileSync("/app/.env", "utf8"); const m = env.match(/^DATABASE_URL=(.+)$/m); let base = m[1].trim().replace(/^["']|["']$/g, ""); if (base.includes("?")) base = base.split("?")[0]; const p = new PrismaClient({ adapter: new PrismaMariaDb(base + "?connection_limit=5&pool_timeout=30") }); const MODE = process.argv[2]; async function dump(outPath) { const [categories, topics, posts, topicLikes, postLikes, tags, topicTags, bookmarks, reports, subscriptions, moderationLogs] = await Promise.all([ p.forumCategory.findMany(), p.forumTopic.findMany(), p.forumPost.findMany(), p.forumTopicLike.findMany(), p.forumPostLike.findMany(), p.forumTag.findMany(), p.forumTopicTag.findMany(), p.forumTopicBookmark.findMany(), p.forumReport.findMany(), p.forumTopicSubscription.findMany(), p.forumModerationLog.findMany(), ]); const data = { categories, topics, posts, topicLikes, postLikes, tags, topicTags, bookmarks, reports, subscriptions, moderationLogs }; fs.writeFileSync(outPath, JSON.stringify(data)); console.log(`DUMP_OK topics=${topics.length} posts=${posts.length} likes=${topicLikes.length + postLikes.length} tags=${tags.length} -> ${outPath} (${(fs.statSync(outPath).size / 1024 / 1024).toFixed(2)}MB)`); } async function clearData() { const del = async (name, fn) => { try { const r = await fn(); console.log(`cleared ${name}: ${r.count ?? r} rows`); return r.count ?? r; } catch (e) { console.log(`clear ${name} skipped: ${e.message.slice(0, 80)}`); return 0; } }; await del("forum_topic_subscriptions", () => p.forumTopicSubscription.deleteMany()); await del("forum_reports", () => p.forumReport.deleteMany()); await del("forum_moderation_logs", () => p.forumModerationLog.deleteMany()); await del("forum_topic_bookmarks", () => p.forumTopicBookmark.deleteMany()); await del("forum_topic_tags", () => p.forumTopicTag.deleteMany()); await del("forum_post_likes", () => p.forumPostLike.deleteMany()); await del("forum_topic_likes", () => p.forumTopicLike.deleteMany()); await del("forum_posts", () => p.forumPost.deleteMany()); await del("forum_topics", () => p.forumTopic.deleteMany()); await del("forum_tags", () => p.forumTag.deleteMany()); const upd = await p.forumCategory.updateMany({ data: { topicCount: 0 } }); console.log(`reset forum_categories.topic_count: ${upd.count}`); const [tc, pc] = await Promise.all([p.forumTopic.count(), p.forumPost.count()]); console.log(`FINAL topics=${tc} posts=${pc}`); } (async () => { if (MODE === "dump") { await dump(process.argv[3] || "/tmp/forum-dump.json"); } else if (MODE === "clear") { await clearData(); } else { console.error("usage: dump | clear"); process.exit(1); } })().catch((e) => { console.error("ERR:", e.message); process.exit(1); }).finally(() => p.$disconnect());