Files
jiang13-forum/scripts/seed-interactions.mjs
freefire b451703642 完善论坛配置与发帖体验:TipTap 富文本、图片上传、修订历史、Feed 排序与后台参数管理。
同步更新 README 与 ROADMAP,反映最新功能与开发状态。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 04:11:38 +08:00

105 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 补全评论、点赞、置顶/锁定(帖子已存在时使用)
* 用法node scripts/seed-interactions.mjs
*/
const BASE = process.env.SEED_BASE || 'http://localhost:3000';
let cookie = '';
function captureCookie(res) {
const raw = res.headers.getSetCookie?.() ?? [];
for (const line of raw) {
const part = line.split(';')[0].trim();
if (part.startsWith('jiang13_token=')) cookie = part;
}
}
async function request(path, opts = {}) {
const headers = { ...(opts.headers || {}) };
if (cookie) headers.Cookie = cookie;
if (opts.body instanceof FormData) delete headers['Content-Type'];
const res = await fetch(`${BASE}${path}`, { ...opts, headers });
captureCookie(res);
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || `${res.status} ${path}`);
return data;
}
async function login(username, password) {
const fd = new FormData();
fd.append('username', username);
fd.append('password', password);
await request('/api/login', { method: 'POST', body: fd });
}
async function addComment(postId, content, replyTo) {
const fd = new FormData();
fd.append('content', content);
if (replyTo) fd.append('reply_to', String(replyTo));
const data = await request(`/api/posts/${postId}/comments`, { method: 'POST', body: fd });
return data.id;
}
async function likePost(postId) {
await request(`/api/posts/${postId}/like`, { method: 'POST' });
}
async function pinPost(postId) {
await request(`/api/admin/posts/${postId}/pin`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pinned: true }),
});
}
async function lockPost(postId) {
await request(`/api/admin/posts/${postId}/lock`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ locked: true }),
});
}
async function main() {
const { posts } = await request('/api/posts?page=1&size=50');
const list = posts ?? [];
if (!list.length) throw new Error('没有帖子,请先运行 node scripts/seed.mjs');
const byTitle = (kw) => list.find((p) => p.title.includes(kw));
const first = list[0];
const qa = byTitle('embed') ?? first;
const hot = list.filter((p) => p.title.includes('热门候选'));
const lockTarget = byTitle('夜班') ?? list.at(-1);
console.log('补全评论与互动…');
await login('alice', 'alice123');
await addComment(first.id, '第一条评论,测试楼层显示。');
const aliceC = await addComment(first.id, '同意,排版帖很有参考价值。');
await addComment(qa.id, '同问embed 路径我也踩过坑。');
await login('bob', 'bob123');
await addComment(first.id, '引用回复测试', aliceC);
for (const p of hot.slice(0, 2)) await addComment(p.id, '热门帖评论一条。');
await login('admin', 'admin123');
await addComment(first.id, '管理员也来回复一楼。');
for (const p of hot) await likePost(p.id);
await login('alice', 'alice123');
for (const p of hot) await likePost(p.id);
await login('bob', 'bob123');
for (const p of hot) await likePost(p.id);
await login('admin', 'admin123');
await pinPost(first.id);
if (list[1]) await pinPost(list[1].id);
if (lockTarget) await lockPost(lockTarget.id);
const stats = await request('/api/stats');
console.log(`完成。帖子 ${stats.posts} · 会员 ${stats.users}`);
}
main().catch((e) => {
console.error('失败:', e.message);
process.exit(1);
});