ed8bc6d1b1
Expose SEO/GEO switches on the MindSpace config form and add template catalog management UI wired to shared Memind modules. Co-authored-by: Cursor <cursoragent@cursor.com>
129 lines
4.8 KiB
JavaScript
129 lines
4.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Local smoke test for page template catalog admin API (memind_adm 8085).
|
|
* Usage: node scripts/verify-template-catalog-local.mjs
|
|
*/
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const base = process.env.ADM_DEV_BACKEND ?? 'http://127.0.0.1:8085';
|
|
|
|
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();
|
|
let value = trimmed.slice(eq + 1).trim();
|
|
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
value = value.slice(1, -1);
|
|
}
|
|
if (!(key in process.env)) process.env[key] = value;
|
|
}
|
|
}
|
|
|
|
loadEnvFile(path.join(root, '.env'));
|
|
|
|
const username = process.env.H5_ADMIN_USERNAME?.trim() || 'admin';
|
|
const password = process.env.H5_ADMIN_PASSWORD?.trim() || process.env.ADMIN_PASSWORD?.trim() || '';
|
|
|
|
async function request(method, urlPath, { body, cookie } = {}) {
|
|
const res = await fetch(`${base}${urlPath}`, {
|
|
method,
|
|
headers: {
|
|
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|
...(cookie ? { Cookie: cookie } : {}),
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined,
|
|
});
|
|
const text = await res.text();
|
|
let json = null;
|
|
try {
|
|
json = text ? JSON.parse(text) : null;
|
|
} catch {
|
|
json = { raw: text.slice(0, 300) };
|
|
}
|
|
return { status: res.status, json, setCookie: res.headers.getSetCookie?.() ?? [] };
|
|
}
|
|
|
|
function cookieFromSetCookie(setCookie) {
|
|
return setCookie.map((c) => c.split(';')[0]).join('; ');
|
|
}
|
|
|
|
function assertOk(cond, msg) {
|
|
if (!cond) throw new Error(msg);
|
|
}
|
|
|
|
async function main() {
|
|
console.log(`==> Admin API base: ${base}`);
|
|
|
|
if (!password) {
|
|
console.error('缺少 H5_ADMIN_PASSWORD(请在 memind_adm/.env 配置)');
|
|
process.exit(1);
|
|
}
|
|
|
|
const login = await request('POST', '/auth/login', {
|
|
body: { username, password },
|
|
});
|
|
assertOk(login.status === 200, `登录失败 HTTP ${login.status}: ${JSON.stringify(login.json)}`);
|
|
const cookie = cookieFromSetCookie(login.setCookie);
|
|
assertOk(cookie, '登录未返回 session cookie');
|
|
console.log(`✓ 管理员登录 (${username})`);
|
|
|
|
const catalog = await request('GET', '/admin-api/template-catalog', { cookie });
|
|
assertOk(catalog.status === 200, `template-catalog HTTP ${catalog.status}: ${JSON.stringify(catalog.json)}`);
|
|
assertOk(Array.isArray(catalog.json?.items), 'items 应为数组');
|
|
assertOk(catalog.json.items.length >= 3, `应至少 seed 3 个 page-template skill,实际 ${catalog.json.items.length}`);
|
|
const travel = catalog.json.items.find((item) => item.skillName === 'page-template-travel');
|
|
assertOk(travel, '缺少 page-template-travel');
|
|
console.log(
|
|
`✓ GET /admin-api/template-catalog (${catalog.json.items.length} 项,travel=${travel.priceCents} 分)`,
|
|
);
|
|
|
|
const originalSort = travel.sortOrder ?? 10;
|
|
const patchedSort = originalSort === 11 ? 12 : 11;
|
|
const update = await request('PUT', '/admin-api/template-catalog/page-template-travel', {
|
|
cookie,
|
|
body: { sortOrder: patchedSort },
|
|
});
|
|
assertOk(update.status === 200, `update HTTP ${update.status}: ${JSON.stringify(update.json)}`);
|
|
assertOk(update.json?.item?.sortOrder === patchedSort, 'sortOrder 未更新');
|
|
console.log(`✓ PUT sortOrder ${originalSort} → ${patchedSort}`);
|
|
|
|
const restore = await request('PUT', '/admin-api/template-catalog/page-template-travel', {
|
|
cookie,
|
|
body: { sortOrder: originalSort },
|
|
});
|
|
assertOk(restore.status === 200, `restore HTTP ${restore.status}`);
|
|
console.log(`✓ PUT sortOrder 恢复为 ${originalSort}`);
|
|
|
|
const users = await request('GET', '/admin-api/users?page=1&pageSize=5&role=user', { cookie });
|
|
assertOk(users.status === 200, `users HTTP ${users.status}`);
|
|
const sampleUser = users.json?.users?.[0];
|
|
assertOk(sampleUser?.id, '需要至少一个 user 账号做授权测试');
|
|
|
|
const grant = await request(
|
|
'POST',
|
|
`/admin-api/users/${sampleUser.id}/template-grants/page-template-travel`,
|
|
{ cookie },
|
|
);
|
|
assertOk(grant.status === 200, `grant HTTP ${grant.status}: ${JSON.stringify(grant.json)}`);
|
|
assertOk(grant.json?.ok === true, 'grant 应返回 ok:true');
|
|
assertOk(
|
|
Array.isArray(grant.json?.grantedSkills) && grant.json.grantedSkills.includes('page-template-travel'),
|
|
'grant 应包含 page-template-travel',
|
|
);
|
|
console.log(`✓ POST template-grants → user ${sampleUser.username}`);
|
|
|
|
console.log('\n全部 template-catalog Admin API 联调通过。');
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('\n联调失败:', err.message);
|
|
process.exit(1);
|
|
});
|