Production readiness hardening and ops tooling

This commit is contained in:
2026-05-29 10:10:12 +07:00
parent e0b8f9af9a
commit 648e77cee9
68 changed files with 12222 additions and 848 deletions

60
scripts/ui-qa-check.mjs Normal file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
const pages = [
"ui/admin-reconciliation-management/index.html",
"ui/admin-system-audit-logs/index.html",
"ui/settlement-batch-management/index.html",
"ui/merchant-settlement-history/index.html",
"ui/device-technical-detail/index.html"
];
const checks = [];
const warnings = [];
const errors = [];
const scriptRe = new RegExp("<script(?:\\s[^>]*)?>([\\s\\S]*?)</script>", "gi");
function check(condition, message) {
checks.push({ ok: Boolean(condition), message });
if (!condition) {
errors.push(message);
}
}
function warn(condition, message) {
if (!condition) {
warnings.push(message);
}
}
for (const page of pages) {
const filePath = path.resolve(process.cwd(), page);
const html = fs.readFileSync(filePath, "utf8");
const scripts = [...html.matchAll(scriptRe)].map((match) => match[1].trim()).filter(Boolean);
for (const [index, script] of scripts.entries()) {
try {
new Function(script);
check(true, `${page} script ${index + 1} parses`);
} catch (error) {
check(false, `${page} script ${index + 1} parse failed: ${error.message}`);
}
}
warn(!/href="#"/.test(html), `${page} still has placeholder # links for manual navigation QA`);
}
const reconciliation = fs.readFileSync("ui/admin-reconciliation-management/index.html", "utf8");
check(reconciliation.includes("adjustment-export-history"), "reconciliation export history is present");
check(!reconciliation.includes("/admin/settlement-adjustments/export.csv?"), "reconciliation uses async export instead of sync CSV URL");
const settlement = fs.readFileSync("ui/settlement-batch-management/index.html", "utf8");
check(settlement.includes('data-admin-permission="settlement:export"'), "settlement export button is permission-aware");
check(settlement.includes('data-admin-permission="settlement:pay"'), "settlement pay action is permission-aware");
const auditLogs = fs.readFileSync("ui/admin-system-audit-logs/index.html", "utf8");
check(auditLogs.includes("audit-action-filter"), "audit logs page has action filter");
check(auditLogs.includes("admin.login.failed"), "audit logs page supports admin login failed filter");
check(auditLogs.includes("merchant.login.failed"), "audit logs page supports merchant login failed filter");
console.log(JSON.stringify({ ok: errors.length === 0, checks, warnings, errors }, null, 2));
process.exit(errors.length ? 1 : 0);