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:
+7
-4
@@ -115,7 +115,7 @@ function loadRecords() {
|
||||
var tr = el('tr');
|
||||
var photos = it.photos || {};
|
||||
var thumb = photos.front
|
||||
? '<img class="thumb" src="' + photos.front + '" alt="正脸">'
|
||||
? '<img class="thumb" loading="lazy" decoding="async" src="' + photos.front + '" alt="正脸">'
|
||||
: '<span class="sub">无</span>';
|
||||
tr.innerHTML =
|
||||
'<td>' + thumb + '</td>' +
|
||||
@@ -321,7 +321,7 @@ function renderDetail(data) {
|
||||
var u = data.photos[w];
|
||||
if (!u) continue;
|
||||
anyPhoto = true;
|
||||
photosHtml += '<div class="d-photo"><img src="' + u + '" alt="' + whichNames[w] + '" data-zoom="' + u + '">' +
|
||||
photosHtml += '<div class="d-photo"><img loading="lazy" decoding="async" src="' + u + '" alt="' + whichNames[w] + '" data-zoom="' + u + '">' +
|
||||
'<a href="' + u + '" download="' + r.record_id + '-' + w + '.jpg">下载' + whichNames[w] + '</a></div>';
|
||||
}
|
||||
photosHtml += anyPhoto ? '' : '<span class="sub">无照片</span>';
|
||||
@@ -429,9 +429,12 @@ function renderDetail(data) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 接口原始 JSON:details 折叠,pre 展示 */
|
||||
/* 接口原始 JSON:details 折叠,pre 展示(face_maps 已在服务端裁剪,完整版走下载链接) */
|
||||
if (data.raw_json) {
|
||||
h += dSec('接口原始 JSON', '<details><summary>展开/收起(raw_json,' + (data.raw_file || '') + ')</summary><pre>' +
|
||||
var rawDl = data.raw_file
|
||||
? ' <a href="/api/admin/records/' + encodeURIComponent(r.record_id) + '/raw" download>下载完整原始 JSON</a>'
|
||||
: '';
|
||||
h += dSec('接口原始 JSON', '<details><summary>展开/收起(' + (data.raw_file || '') + ')' + rawDl + '</summary><pre>' +
|
||||
esc(JSON.stringify(data.raw_json, null, 1)) + '</pre></details>');
|
||||
} else {
|
||||
h += dSec('接口原始 JSON', '<div class="sub">无(失败记录或未落盘)</div>');
|
||||
|
||||
+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)$/);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# ~/.openclaw/credentials.env — 统一凭据存放(勿提交 git、勿发到聊天)
|
||||
# 用法: set -a; source ~/.openclaw/credentials.env; set +a
|
||||
|
||||
# ---- 旷视 Face++(巨子测肤 skinanalyze_pro 测试接口)----
|
||||
# 试用key(cAa5...)已弃用: 旷视规则试用key不能调专业版。当前为正式key(EcZ...)
|
||||
FACEPP_API_KEY=EcZiqYhA8eQSiRgXVem8mijgzPfSC2qp
|
||||
FACEPP_API_SECRET=p62QRWv9tQitZ1tvXmEBV8AgmaS00lO8
|
||||
|
||||
# ---- FaceStyle 专业版调用参数 ----
|
||||
FACEPP_SKINANALYZE_URL=https://api-cn.faceplusplus.com/facepp/v1/skinanalyze_pro
|
||||
|
||||
# ---- 飞书 / OCR(合同盖章核验项目)----
|
||||
# 实际值在 ~/Desktop/合同盖章核验项目/service/.env(未入库)
|
||||
|
||||
# ---- 智谱 / 火山引擎 ----
|
||||
# 已存于 ~/.zshrc 及 openclaw.json providers 配置
|
||||
Reference in New Issue
Block a user