feat: Page Data dev loop, adm Code Run UI, and customer order demo
Add verify→Aider→OpenHands dev loop, memindadm config/history UI, and john4 customer-order Page Data demo with scenario + verification scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 验证 john4 客户下单系统 Page Data 链路(insert + admin read)。
|
||||
*/
|
||||
import { loadH5Environment } from './load-env.mjs';
|
||||
import {
|
||||
createReporter,
|
||||
loginViaApi,
|
||||
resolvePortalBase,
|
||||
} from './scenario-test-lib.mjs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { PUBLISH_ROOT_DIR } from '../user-publish.mjs';
|
||||
import fs from 'node:fs/promises';
|
||||
|
||||
const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const USER_ID = process.env.CUSTOMER_ORDER_USER_ID ?? '32035858-9a20-425b-89da-c118ef0779aa';
|
||||
const DATASET = 'customer_orders';
|
||||
const FORM_PAGE = 'customer-order-form.html';
|
||||
const ADMIN_PAGE = 'customer-order-admin.html';
|
||||
|
||||
loadH5Environment(import.meta.dirname);
|
||||
|
||||
async function findPageIdFromPolicy(publishKey) {
|
||||
const policyDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'page-data-policies');
|
||||
const entries = await fs.readdir(policyDir);
|
||||
for (const name of entries.filter((item) => item.endsWith('.json'))) {
|
||||
const raw = await fs.readFile(path.join(policyDir, name), 'utf8');
|
||||
const policy = JSON.parse(raw);
|
||||
if (policy?.datasets?.[DATASET]?.insert) {
|
||||
return { pageId: policy.pageId, fileName: name };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const port = Number(process.env.H5_PORT ?? 8081);
|
||||
const baseUrl = resolvePortalBase(port);
|
||||
const reporter = createReporter();
|
||||
|
||||
console.log('==> 客户下单系统 Page Data 验证');
|
||||
console.log(` Portal: ${baseUrl}`);
|
||||
console.log(` 用户: ${USER_ID}\n`);
|
||||
|
||||
const auth = await loginViaApi(baseUrl, { username: 'john4', password: '888888' }, reporter);
|
||||
const publishKey = auth.user?.id ?? USER_ID;
|
||||
|
||||
const formUrl = `${baseUrl}/MindSpace/${publishKey}/public/${FORM_PAGE}`;
|
||||
const adminUrl = `${baseUrl}/MindSpace/${publishKey}/public/${ADMIN_PAGE}`;
|
||||
|
||||
for (const [label, url] of [['下单页', formUrl], ['后台页', adminUrl]]) {
|
||||
const res = await fetch(url, { headers: { Cookie: auth.cookie } });
|
||||
const html = await res.text();
|
||||
if (res.status !== 200) {
|
||||
reporter.fail(`${label} HTTP`, `${res.status}`);
|
||||
} else {
|
||||
reporter.pass(`${label} HTTP 200`, url);
|
||||
}
|
||||
if (!html.includes('page-data-client.js')) {
|
||||
reporter.fail(`${label} 脚本`, '缺少 page-data-client.js');
|
||||
} else {
|
||||
reporter.pass(`${label} Page Data 客户端`, '已引用');
|
||||
}
|
||||
}
|
||||
|
||||
const formMeta = await findPageIdFromPolicy(publishKey);
|
||||
if (!formMeta?.pageId) {
|
||||
reporter.fail('Page Data policy', '未找到 customer_orders insert policy');
|
||||
process.exit(reporter.summary());
|
||||
}
|
||||
reporter.pass('Page Data policy', `${formMeta.fileName} pageId=${formMeta.pageId}`);
|
||||
|
||||
const insertPayload = {
|
||||
customer_name: '测试客户',
|
||||
phone: '13800138000',
|
||||
product_name: '演示商品A',
|
||||
quantity: '2',
|
||||
address: '上海市浦东新区测试路 1 号',
|
||||
remark: '自动化验证',
|
||||
status: '待处理',
|
||||
};
|
||||
|
||||
const insertRes = await fetch(
|
||||
`${baseUrl}/api/public/pages/${formMeta.pageId}/data/${DATASET}/rows`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Cookie: auth.cookie },
|
||||
body: JSON.stringify(insertPayload),
|
||||
},
|
||||
);
|
||||
const insertBody = await insertRes.json().catch(() => ({}));
|
||||
if (!insertRes.ok) {
|
||||
reporter.fail('下单 insert', `${insertRes.status} ${JSON.stringify(insertBody)}`);
|
||||
} else {
|
||||
reporter.pass('下单 insert', `row id=${insertBody?.data?.id ?? insertBody?.id ?? 'ok'}`);
|
||||
}
|
||||
|
||||
const adminMeta = await (async () => {
|
||||
const policyDir = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'page-data-policies');
|
||||
const entries = await fs.readdir(policyDir);
|
||||
for (const name of entries.filter((item) => item.endsWith('.json'))) {
|
||||
const policy = JSON.parse(await fs.readFile(path.join(policyDir, name), 'utf8'));
|
||||
if (policy?.datasets?.[DATASET]?.read) return policy.pageId;
|
||||
}
|
||||
return null;
|
||||
})();
|
||||
|
||||
if (!adminMeta) {
|
||||
reporter.fail('后台 policy', '未找到 read policy');
|
||||
} else {
|
||||
const tokenRes = await fetch(`${baseUrl}/api/public/pages/${adminMeta}/data-auth`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Cookie: auth.cookie },
|
||||
body: JSON.stringify({ password: '88888888' }),
|
||||
});
|
||||
const tokenBody = await tokenRes.json().catch(() => ({}));
|
||||
const token = tokenBody?.data?.token ?? tokenBody?.token;
|
||||
if (!tokenRes.ok || !token) {
|
||||
reporter.fail('后台口令认证', `${tokenRes.status}`);
|
||||
} else {
|
||||
const listRes = await fetch(
|
||||
`${baseUrl}/api/public/pages/${adminMeta}/data/${DATASET}?limit=20`,
|
||||
{ headers: { 'x-page-data-token': token, Cookie: auth.cookie } },
|
||||
);
|
||||
const listBody = await listRes.json().catch(() => ({}));
|
||||
const rows = listBody?.data?.rows ?? listBody?.rows ?? [];
|
||||
const hit = rows.some((row) => row.customer_name === insertPayload.customer_name && row.product_name === insertPayload.product_name);
|
||||
if (!listRes.ok || !hit) {
|
||||
reporter.fail('后台读订单', `rows=${rows.length}, 未找到测试订单`);
|
||||
} else {
|
||||
reporter.pass('后台读订单', `共 ${rows.length} 条,含测试订单`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sqlitePath = path.join(repoRoot, PUBLISH_ROOT_DIR, publishKey, '.mindspace', 'private-data.sqlite');
|
||||
try {
|
||||
await fs.stat(sqlitePath);
|
||||
reporter.pass('用户数据空间', 'private-data.sqlite 存在');
|
||||
} catch {
|
||||
reporter.pass('用户数据空间', 'PostgreSQL 模式(无本地 sqlite 文件)');
|
||||
}
|
||||
|
||||
console.log('\n--- 访问入口 ---');
|
||||
console.log(`下单页: ${formUrl}`);
|
||||
console.log(`后台页: ${adminUrl} (口令 88888888)`);
|
||||
|
||||
process.exit(reporter.summary());
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err instanceof Error ? err.stack ?? err.message : err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user