chore+perf: 密钥出库 + 后台详情接口裁剪 face_maps
安全:
- reference/facepp-skinanalyze/credentials.env 内含 Face++ 正式 key/secret
且被 git 跟踪,移出 index(文件保留本地,.gitignore 已覆盖 *.env)。
⚠️ 密钥已存在于 git 历史,若 origin 曾被 push 即视为泄漏,
请在旷视控制台重置 API secret,改用环境变量/apikey.bash 注入。
性能(后台详情页卡顿):
- 详情接口将 raw_json.result.face_maps 的 base64 图层(单条约 3MB)
替换为占位说明,响应 3.2MB→163KB;新增
GET /api/admin/records/:id/raw 供下载完整原始 JSON(走 Basic Auth)
- admin 列表/详情图片加 loading=lazy + decoding=async
This commit is contained in:
+29
-1
@@ -524,13 +524,23 @@ function handleRecordsQuery(req, res, url) {
|
||||
function handleRecordDetail(req, res, id) {
|
||||
const rec = findRecord(id);
|
||||
if (!rec) return sendJson(res, 404, { ok: false, message: '记录不存在' });
|
||||
// 原始 JSON 全文:读文件失败给 null
|
||||
// 原始 JSON 全文:读文件失败给 null。
|
||||
// face_maps 是整幅 base64 叠加图(单文件可达数 MB),详情页直接渲染会卡死浏览器:
|
||||
// 展示层把各图层值替换为占位说明,完整原文件走 /api/admin/records/:id/raw 下载
|
||||
let rawJson = null;
|
||||
if (rec.raw_file) {
|
||||
const fname = String(rec.raw_file).replace(/[^\w.,-]/g, '');
|
||||
try { rawJson = JSON.parse(fs.readFileSync(path.join(DATA_DIR, fname), 'utf8')); }
|
||||
catch (e) { rawJson = null; }
|
||||
}
|
||||
if (rawJson && rawJson.result && rawJson.result.face_maps) {
|
||||
for (const k of Object.keys(rawJson.result.face_maps)) {
|
||||
const v = rawJson.result.face_maps[k];
|
||||
if (typeof v === 'string' && v.length) {
|
||||
rawJson.result.face_maps[k] = `<base64 图层已省略,共 ${v.length} 字符,点上方链接下载完整原文件>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 版本链:它 retry_of 指向的(父)+ retry_of 指向它的(子)
|
||||
const all = readAllRecords();
|
||||
const parent = rec.retry_of ? (all.find(r => r.record_id === rec.retry_of) || null) : null;
|
||||
@@ -548,6 +558,22 @@ function handleRecordDetail(req, res, id) {
|
||||
});
|
||||
}
|
||||
|
||||
/* 完整原始 JSON 下载(face_maps base64 未裁剪,动辄数 MB,仅供复核下载,不做页面内展示) */
|
||||
function handleRawDownload(req, res, id) {
|
||||
const rec = findRecord(id);
|
||||
if (!rec || !rec.raw_file) return sendJson(res, 404, { ok: false, message: '记录或原始 JSON 不存在' });
|
||||
const fname = String(rec.raw_file).replace(/[^\w.,-]/g, '');
|
||||
const file = path.join(DATA_DIR, fname);
|
||||
fs.readFile(file, (err, data) => {
|
||||
if (err) return sendJson(res, 404, { ok: false, message: '原始 JSON 文件不存在' });
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'Content-Disposition': `attachment; filename="${rec.record_id}-raw.json"`
|
||||
});
|
||||
res.end(data);
|
||||
});
|
||||
}
|
||||
|
||||
function handlePhoto(req, res, id, which) {
|
||||
const safe = String(id || '').replace(/[^\w-]/g, '');
|
||||
const file = path.join(PHOTOS_DIR, safe, which + '.jpg');
|
||||
@@ -710,6 +736,8 @@ async function handleAdmin(req, res, pathname, url) {
|
||||
try {
|
||||
const retryM = pathname.match(/^\/api\/admin\/records\/([\w-]+)\/retry$/);
|
||||
if (req.method === 'POST' && retryM) return await handleRetry(req, res, retryM[1]);
|
||||
const rawM = pathname.match(/^\/api\/admin\/records\/([\w-]+)\/raw$/);
|
||||
if (req.method === 'GET' && rawM) return handleRawDownload(req, res, rawM[1]);
|
||||
const detailM = pathname.match(/^\/api\/admin\/records\/([\w-]+)$/);
|
||||
if (req.method === 'GET' && detailM) return handleRecordDetail(req, res, detailM[1]);
|
||||
const photoM = pathname.match(/^\/api\/admin\/photos\/([\w-]+)\/(front|left|right)$/);
|
||||
|
||||
Reference in New Issue
Block a user