Files
memind/scripts/purge-plaza-demo.mjs
T
john 70492d9eba Add attachment text extraction, auto web news skill, and chat/voice UI updates.
Simplify asset upload temp paths, refresh deploy docs for Aliyun DNS topology, and ship MindSpace content-scan and auth improvements.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-20 15:08:10 +08:00

115 lines
3.7 KiB
JavaScript

#!/usr/bin/env node
/**
* 删除 Plaza 演示/种子内容(seed-plaza-demo.mjs 写入的数据),保留真实用户发布。
* 识别规则:发布 slug 以 demo- 开头,或内容资产 checksum = demo-seed。
*
* 用法:node scripts/purge-plaza-demo.mjs [--dry-run]
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createDbPool } from '../db.mjs';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const dryRun = process.argv.includes('--dry-run');
function loadEnvFile(filePath) {
if (!fs.existsSync(filePath)) return;
for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const eq = trimmed.indexOf('=');
if (eq < 0) continue;
const key = trimmed.slice(0, eq).trim();
const value = trimmed.slice(eq + 1).trim();
if (!process.env[key]) process.env[key] = value;
}
}
loadEnvFile(path.join(root, '../../.env.local'));
loadEnvFile(path.join(root, '.env'));
const pool = createDbPool();
const conn = await pool.getConnection();
try {
const [rows] = await conn.query(
`SELECT pp.id AS post_id, pp.title, pp.status AS post_status,
pr.id AS publication_id, pr.page_id, pr.page_version_id, pr.url_slug, pr.security_scan_id,
u.username
FROM plaza_posts pp
JOIN h5_publish_records pr ON pr.id = pp.publication_id
JOIN h5_page_versions pv ON pv.id = pr.page_version_id
JOIN h5_assets a ON a.id = pv.content_asset_id
JOIN h5_users u ON u.id = pp.user_id
WHERE pr.url_slug LIKE 'demo-%' OR a.checksum = 'demo-seed'
ORDER BY pp.published_at DESC`,
);
if (rows.length === 0) {
console.log('未发现演示内容,无需清理。');
process.exit(0);
}
console.log(`${dryRun ? '[dry-run] ' : ''}将删除 ${rows.length} 条演示广场帖:`);
for (const row of rows) {
console.log(` - [${row.username}] ${row.title} (${row.url_slug})`);
}
if (dryRun) {
process.exit(0);
}
await conn.beginTransaction();
const publicationIds = [...new Set(rows.map((row) => row.publication_id))];
const pageIds = [...new Set(rows.map((row) => row.page_id))];
const scanIds = [...new Set(rows.map((row) => row.security_scan_id).filter(Boolean))];
const postIds = rows.map((row) => row.post_id);
const [postResult] = await conn.query(
`DELETE FROM plaza_posts WHERE id IN (${postIds.map(() => '?').join(',')})`,
postIds,
);
const [pubResult] = await conn.query(
`DELETE FROM h5_publish_records WHERE id IN (${publicationIds.map(() => '?').join(',')})`,
publicationIds,
);
if (pageIds.length > 0) {
await conn.query(
`UPDATE h5_page_records SET current_publish_id = NULL WHERE id IN (${pageIds.map(() => '?').join(',')})`,
pageIds,
);
await conn.query(
`DELETE FROM h5_page_records WHERE id IN (${pageIds.map(() => '?').join(',')})`,
pageIds,
);
}
if (scanIds.length > 0) {
await conn.query(
`DELETE FROM h5_security_scans WHERE id IN (${scanIds.map(() => '?').join(',')})`,
scanIds,
);
}
const [assetResult] = await conn.query(
`DELETE FROM h5_assets WHERE checksum = 'demo-seed'`,
);
await conn.commit();
const [remaining] = await pool.query(`SELECT COUNT(*) AS cnt FROM plaza_posts`);
console.log(`\n完成:删除 ${postResult.affectedRows} 条广场帖、${pubResult.affectedRows} 条发布记录、${assetResult.affectedRows} 个演示资产。`);
console.log(`剩余广场帖:${remaining[0]?.cnt ?? 0} 条。`);
} catch (error) {
await conn.rollback();
console.error('清理失败:', error);
process.exitCode = 1;
} finally {
conn.release();
await pool.end();
}