chore(plaza): add 103 backfill scripts for cover URLs and hero thumbnails
Memind CI / Test, build, and release guards (push) Has been cancelled
Memind CI / Test, build, and release guards (push) Has been cancelled
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,10 +3,14 @@ import fs from 'node:fs';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { createDbPool } from '../db.mjs';
|
import { createDbPool } from '../db.mjs';
|
||||||
import { resolvePlazaCoverUrl } from '../plaza-posts.mjs';
|
import { resolvePlazaPublicationCoverUrl } from '../plaza-posts.mjs';
|
||||||
|
|
||||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
const shouldWrite = process.argv.includes('--write');
|
const shouldWrite = process.argv.includes('--write');
|
||||||
|
const batchPrefix = (() => {
|
||||||
|
const idx = process.argv.indexOf('--batch-prefix');
|
||||||
|
return idx >= 0 ? String(process.argv[idx + 1] ?? '').trim() : '';
|
||||||
|
})();
|
||||||
|
|
||||||
function loadEnvFile(filePath) {
|
function loadEnvFile(filePath) {
|
||||||
if (!fs.existsSync(filePath)) return;
|
if (!fs.existsSync(filePath)) return;
|
||||||
@@ -25,12 +29,23 @@ loadEnvFile(path.join(root, '../../.env.local'));
|
|||||||
loadEnvFile(path.join(root, '.env'));
|
loadEnvFile(path.join(root, '.env'));
|
||||||
|
|
||||||
const pool = createDbPool();
|
const pool = createDbPool();
|
||||||
|
const params = [];
|
||||||
|
let batchFilter = '';
|
||||||
|
if (batchPrefix) {
|
||||||
|
batchFilter = ' AND p.workspace_relative_path LIKE ?';
|
||||||
|
params.push(`public/${batchPrefix}%`);
|
||||||
|
}
|
||||||
|
|
||||||
const [rows] = await pool.query(
|
const [rows] = await pool.query(
|
||||||
`SELECT pp.id, pp.title, pp.cover_url, pp.status, pr.public_url
|
`SELECT pp.id, pp.title, pp.cover_url, pp.user_slug, pp.status,
|
||||||
|
pr.public_url, pr.url_slug
|
||||||
FROM plaza_posts pp
|
FROM plaza_posts pp
|
||||||
JOIN h5_publish_records pr ON pr.id = pp.publication_id
|
JOIN h5_publish_records pr ON pr.id = pp.publication_id
|
||||||
|
JOIN h5_page_records p ON p.id = pr.page_id
|
||||||
WHERE pp.status IN ('published', 'pending_review', 'rejected')
|
WHERE pp.status IN ('published', 'pending_review', 'rejected')
|
||||||
|
${batchFilter}
|
||||||
ORDER BY pp.published_at DESC, pp.id DESC`,
|
ORDER BY pp.published_at DESC, pp.id DESC`,
|
||||||
|
params,
|
||||||
);
|
);
|
||||||
|
|
||||||
const updates = rows
|
const updates = rows
|
||||||
@@ -38,12 +53,19 @@ const updates = rows
|
|||||||
id: row.id,
|
id: row.id,
|
||||||
title: row.title,
|
title: row.title,
|
||||||
from: String(row.cover_url ?? ''),
|
from: String(row.cover_url ?? ''),
|
||||||
to: resolvePlazaCoverUrl(row.cover_url, row.public_url),
|
to: resolvePlazaPublicationCoverUrl({
|
||||||
|
inputCoverUrl: row.cover_url,
|
||||||
|
publicUrl: row.public_url,
|
||||||
|
publicationUrlSlug: row.url_slug,
|
||||||
|
userSlug: row.user_slug,
|
||||||
|
}),
|
||||||
publicUrl: String(row.public_url ?? ''),
|
publicUrl: String(row.public_url ?? ''),
|
||||||
}))
|
}))
|
||||||
.filter((row) => row.to && row.from !== row.to);
|
.filter((row) => row.to && row.from !== row.to);
|
||||||
|
|
||||||
console.log(`${shouldWrite ? '写入模式' : 'Dry-run'}:共扫描 ${rows.length} 条帖子,命中 ${updates.length} 条待补封面。`);
|
console.log(
|
||||||
|
`${shouldWrite ? '写入模式' : 'Dry-run'}:共扫描 ${rows.length} 条帖子,命中 ${updates.length} 条待补封面。`,
|
||||||
|
);
|
||||||
|
|
||||||
for (const row of updates.slice(0, 20)) {
|
for (const row of updates.slice(0, 20)) {
|
||||||
console.log(`- ${row.id} | ${row.title}`);
|
console.log(`- ${row.id} | ${row.title}`);
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* 强制重生成 Plaza 帖对应 workspace HTML 的缩略图(嵌入 hero 图)。
|
||||||
|
*
|
||||||
|
* Usage (103):
|
||||||
|
* node scripts/backfill-plaza-workspace-thumbnails.mjs --dry-run
|
||||||
|
* node scripts/backfill-plaza-workspace-thumbnails.mjs --apply
|
||||||
|
* node scripts/backfill-plaza-workspace-thumbnails.mjs --apply --batch-prefix plaza-batch-
|
||||||
|
*/
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { createDbPool } from '../db.mjs';
|
||||||
|
import { resolveMindSpaceUserPublishDir } from '../mindspace-runtime-config.mjs';
|
||||||
|
import { ensureWorkspaceHtmlThumbnail } from '../mindspace-workspace-thumbnails.mjs';
|
||||||
|
|
||||||
|
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||||
|
|
||||||
|
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'));
|
||||||
|
|
||||||
|
function parseArgs() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
let apply = false;
|
||||||
|
let batchPrefix = '';
|
||||||
|
for (let i = 0; i < args.length; i += 1) {
|
||||||
|
if (args[i] === '--apply') apply = true;
|
||||||
|
else if (args[i] === '--dry-run') apply = false;
|
||||||
|
else if (args[i] === '--batch-prefix' && args[i + 1]) batchPrefix = args[++i];
|
||||||
|
}
|
||||||
|
return { apply, batchPrefix };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { apply, batchPrefix } = parseArgs();
|
||||||
|
const h5Root = process.env.H5_ROOT ?? path.join(root, 'users');
|
||||||
|
const pool = createDbPool();
|
||||||
|
const params = ['published'];
|
||||||
|
let batchFilter = '';
|
||||||
|
if (batchPrefix) {
|
||||||
|
batchFilter = ' AND p.workspace_relative_path LIKE ?';
|
||||||
|
params.push(`public/${batchPrefix}%`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [rows] = await pool.query(
|
||||||
|
`SELECT pp.id AS post_id, pp.title, pp.user_id, p.workspace_relative_path
|
||||||
|
FROM plaza_posts pp
|
||||||
|
JOIN h5_publish_records pr ON pr.id = pp.publication_id
|
||||||
|
JOIN h5_page_records p ON p.id = pr.page_id
|
||||||
|
WHERE pp.status = ?
|
||||||
|
AND p.workspace_relative_path LIKE 'public/%.html'
|
||||||
|
${batchFilter}
|
||||||
|
ORDER BY pp.published_at DESC, pp.id DESC`,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`${apply ? '写入模式' : 'Dry-run'}:命中 ${rows.length} 条 workspace HTML 帖子。`);
|
||||||
|
|
||||||
|
let ok = 0;
|
||||||
|
let skipped = 0;
|
||||||
|
let failed = 0;
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const relativePath = String(row.workspace_relative_path ?? '').trim();
|
||||||
|
if (!relativePath) {
|
||||||
|
skipped += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const publishDir = resolveMindSpaceUserPublishDir(h5Root, { id: row.user_id });
|
||||||
|
const htmlPath = path.join(publishDir, relativePath);
|
||||||
|
if (!fs.existsSync(htmlPath)) {
|
||||||
|
console.warn(`- SKIP ${row.post_id} | ${row.title} | missing ${relativePath}`);
|
||||||
|
skipped += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
console.log(`- ${row.post_id} | ${row.title} | ${relativePath}`);
|
||||||
|
if (!apply) continue;
|
||||||
|
try {
|
||||||
|
await ensureWorkspaceHtmlThumbnail(publishDir, relativePath, null, {
|
||||||
|
title: row.title,
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
ok += 1;
|
||||||
|
} catch (error) {
|
||||||
|
failed += 1;
|
||||||
|
console.warn(
|
||||||
|
` FAIL ${row.post_id}:`,
|
||||||
|
error instanceof Error ? error.message : error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`完成:regenerated=${ok} skipped=${skipped} failed=${failed}`);
|
||||||
|
if (!apply) {
|
||||||
|
console.log('Dry-run 未写入;正式执行: node scripts/backfill-plaza-workspace-thumbnails.mjs --apply');
|
||||||
|
}
|
||||||
|
|
||||||
|
await pool.end();
|
||||||
Reference in New Issue
Block a user