Files
memind_adm/scripts/verify-image-quota-portal.mjs
john a4d46f2b1e feat(admin): add image quota management UI on memind_adm 5174
Add image-quota admin pages and API wiring for plan defaults, ledger, and per-user
grants, plus local verify scripts and AGENTS.md note that this repo is the sole
admin UI surface.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 14:58:38 +08:00

93 lines
3.2 KiB
JavaScript
Raw Permalink 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.
#!/usr/bin/env node
/** Portal smoke: /auth/me subscription includes image quota fields */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const memindRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '../..', 'Memind');
const portalBase = process.env.PORTAL_BASE ?? 'http://127.0.0.1:8081';
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(memindRoot, '.env'));
const username = process.env.H5_TEST_USERNAME?.trim() || process.env.H5_ADMIN_USERNAME?.trim() || 'admin';
const password = process.env.H5_TEST_PASSWORD?.trim() || process.env.H5_ADMIN_PASSWORD?.trim() || '';
async function request(method, urlPath, { body, cookie } = {}) {
const res = await fetch(`${portalBase}${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, 200) }; }
return { status: res.status, json, setCookie: res.headers.getSetCookie?.() ?? [] };
}
function cookieFromSetCookie(setCookie) {
return setCookie.map((c) => c.split(';')[0]).join('; ');
}
async function main() {
console.log(`==> Portal base: ${portalBase}`);
if (!password) {
console.error('缺少登录密码(Memind/.env 中 H5_ADMIN_PASSWORD 或 H5_TEST_PASSWORD');
process.exit(1);
}
const login = await request('POST', '/auth/login', { body: { username, password } });
if (login.status !== 200) {
console.error(`登录失败 HTTP ${login.status}`);
process.exit(1);
}
const cookie = cookieFromSetCookie(login.setCookie);
console.log(`✓ Portal 登录 (${username})`);
const me = await request('GET', '/auth/me', { cookie });
if (me.status !== 200) {
console.error(`/auth/me HTTP ${me.status}`);
process.exit(1);
}
const sub = me.json?.user?.subscription;
if (!sub) {
console.log('⚠ 当前用户无 active subscription(免费用户可能无订阅记录)');
process.exit(0);
}
const fields = ['periodImagesLimit', 'periodImagesUsed', 'periodImagesBonus'];
for (const f of fields) {
if (!(f in sub)) {
console.error(`subscription 缺少字段: ${f}`);
process.exit(1);
}
}
console.log(
`✓ /auth/me subscription 图片额度: limit=${sub.periodImagesLimit} bonus=${sub.periodImagesBonus ?? 0} used=${sub.periodImagesUsed}`,
);
console.log('\nPortal 用户侧 subscription 字段联调通过。');
}
main().catch((err) => {
console.error('联调失败:', err.message);
process.exit(1);
});