本地长期开发成果的整体入库快照,包含后端鉴权/权限、AI 接入、合同解析、 SKU 目录等模块,以及前端相应页面与样式改版。 后端 - 新增鉴权与会话:scrypt 密码哈希、HttpOnly cookie(happy_sid)、登录失败锁定 - 新增「角色 → 权限组 → 权限点」三级权限模型与审计日志 - 新增 AI 提供商接入与预设(兼容 OpenAI 协议,内置 DeepSeek / 通义千问等) - 新增合同解析、合同文件保留策略、客户归属与访问控制 - 新增 SKU 目录、组织品牌、新客开发、系统日志等路由 前端 - 新增登录页与用户 / 权限组 / 系统日志管理页 - 新增客户详情、客户公海、询盘详情、报价编辑器、合同清洗中心 - 新增 AI 配置、组织品牌、包装参数、SKU 选择器等组件 - 新增英文大写金额、未保存改动拦截、抽屉与骨架屏 - 新增 stage1 样式层;产品、供应商、订单等页面改版 - EmbeddedSku 页面并入现有产品体系后删除 开源准备 - 新增 AGPL-3.0 LICENSE - README:移除未启用的 SQLCipher 加密声明(改为如实的「已知限制」)、 补充环境变量说明、许可证由「私有项目」改为 AGPL-3.0 - 移除历史遗留的空文件 _index_backup.txt - 文档中的项目路径更新为 happy-export
63 lines
1.9 KiB
JavaScript
63 lines
1.9 KiB
JavaScript
/**
|
|
* 系统信息与版本更新检查。
|
|
* 挂载点:/api/system
|
|
*/
|
|
const express = require('express');
|
|
const updater = require('../lib/updater');
|
|
const branding = require('../lib/branding');
|
|
|
|
const router = express.Router();
|
|
|
|
// 简易缓存:GitHub 未认证请求每小时只有 60 次配额,连续点「检查更新」不该把额度刷完。
|
|
const CACHE_TTL_MS = 30 * 1000;
|
|
let cache = { key: '', at: 0, payload: null };
|
|
|
|
/**
|
|
* GET /api/system/info
|
|
* 当前版本与运行环境。轻量、不发外部请求,设置页打开时就能调。
|
|
*/
|
|
router.get('/info', (req, res) => {
|
|
try {
|
|
res.json(updater.systemInfo());
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/system/check-update?repo=owner/repo&force=1
|
|
* 对比远端仓库版本与本机版本。repo 省略时用默认仓库(Drsakura/happy-export)。
|
|
*/
|
|
router.get('/check-update', async (req, res) => {
|
|
try {
|
|
const repo = updater.normalizeRepo(req.query.repo) || updater.DEFAULT_REPO;
|
|
const force = String(req.query.force || '') === '1';
|
|
|
|
if (!force && cache.payload && cache.key === repo && Date.now() - cache.at < CACHE_TTL_MS) {
|
|
return res.json({ ...cache.payload, cached: true });
|
|
}
|
|
|
|
const result = await updater.checkUpdate(repo);
|
|
cache = { key: repo, at: Date.now(), payload: result };
|
|
res.json({ ...result, cached: false });
|
|
} catch (error) {
|
|
// checkUpdate 内部已兜底,走到这里说明是意料之外的问题
|
|
res.status(500).json({ ok: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/system/branding
|
|
* 品牌与组织名。公开接口 —— 登录页在拿到会话之前就得知道系统叫什么。
|
|
* 只回显示用的名字,不含任何业务数据。
|
|
*/
|
|
router.get('/branding', (req, res) => {
|
|
try {
|
|
res.json(branding.getBranding());
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|