会员服务器:上传肥牛应用源码 v1.5.1(fnnas.huiyuan 完整 fpk 工程结构)

含 app/server/server.cjs 服务端、app/www 网页(admin/auth/项目云/库备份/settings)、
app/ui 图标、cmd 安装脚本、config、wizard、manifest、图标与对接文档。
按仓库规则排除构建产物 *.fpk 与 .mimosa 缓存。
This commit is contained in:
dqb-dev
2026-10-02 00:03:20 +08:00
parent d9be73be89
commit 65755bf520
30 changed files with 2718 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
# fnnas.huiyuan 会员服务器(飞牛 fnOS 应用)
成套报价软件的会员系统服务器,打包为飞牛 fnOS 原生应用(.fpk)。v1.4.0 起全站强制 HTTPS(自签证书)+ 应用层信封加密。
- **产物**:`fnnas.huiyuan_1.4.0_all.fpk`
- **默认端口**:12701(安装向导可改)
- **运行时**:飞牛自带 Node.js(`install_dep_apps=nodejs_v22`),零第三方依赖
- **源码来源**:`/vol2/1000/fnosyy/会员服务器/`(server.cjs 改造 + www 页面移植)
## 功能
| 功能 | 说明 |
|---|---|
| 客户端 API | 凭据类(登录/注册/查会员/改密)走 /api/enc 信封加密;项目云走 TLS 直连;业务字段与原版一致 |
| 项目云 API | `/api/proj/upload`(JSON头+空行+zip 二进制,覆盖式,上限 2GB)、`/api/proj/list`、`/api/proj/download`、`/api/proj/delete`;上传校验会员、文件名安全化、防路径穿越 |
| 会员管理页 | `http://NAS_IP:端口/`(admin.html) |
| 项目云管理页 | `/xiangmu-cloud.html` 按会员查看/下载/删除云上项目 |
| 库备份管理页 | `/lib-cloud.html` 只显示 LIB_ 前缀条目(方案库/用户库/柜体库/铜排铝排库/企业信息) |
| 管理登录 | 四页面需管理密码登录(安装向导设置),设置页可改密码/关闭注册 |
| 设置页 | `/settings.html` 显示端口/版本/会员数/数据库文件/项目云目录,切换数据目录(连库+项目云一起迁移) |
| 安装向导 | 端口(默认 12701)+ 数据目录(可选,留空=默认)+ 管理密码(必填 ≥6 位) |
| 全站加密 | 唯一 HTTPS 端口;TLS 自签证书自动生成;信封加密(RSA+AES+HMAC+防重放);证书可在设置页下载 |
| 装后改端口 | 应用中心 → 会员服务器 → 应用设置(留空=不修改) |
| 目录授权 | 应用设置 → 目录授权,或设置页"授权新目录"按钮(管理员,需 fnOS ≥ 1.2.0401) |
## 客户端接口(v1.2.0 起项目云需带密码——客户端必须同步升级)
| 接口 | 客户端改动 |
|---|---|
| `POST /api/login` | 不变 |
| `POST /api/register` | 不变(服务端校验 11 位手机号、密码 4-64 位) |
| `GET /api/member` | 不变 |
| `POST /api/changepwd` | 不变 |
| `POST /api/proj/upload` | JSON 头新增 `"密码": "该会员密码"` |
| `GET /api/proj/list` | URL 新增 `&密码=<该会员密码>`(URL 编码) |
| `GET /api/proj/download` | 同上 |
| `POST /api/proj/delete` | body 新增 `"密码": "该会员密码"` |
不带上传/查询密码的项目云请求会返回 `{ok:false, msg:"手机号或密码错误"}`。
## 全站 HTTPS 与双层加密(v1.4.0)
**全站只有一个 HTTPS 端口**(默认 12701,安装向导可改),HTTP 明文通道已彻底停用——浏览器打开 `http://IP:端口` 无法连接,必须使用 `https://IP:端口`。
- 自签证书首次启动自动生成(OpenSSL,存应用配置目录);设置页可查看 TLS 证书指纹 / RSA 公钥指纹并**下载证书**(安装到 Windows"受信任的根证书颁发机构"后浏览器不再警告)
- 安全响应头:HSTS(max-age 1 年)、nosniff、X-Frame-Options
- TLS 初始化失败服务拒绝启动(强制 HTTPS 是硬性要求)
### 第 2 层:应用层信封加密(客户端凭据类通讯)
即使 TLS 被绕过(如中间人持自签证书),业务数据仍是密文:
```
① GET /api/pubkey (HTTPS) → 服务器 RSA-2048 公钥 + 公钥指纹
② 每次请求(登录/注册/查会员/改密/项目云列表/删除):
aesKey = 随机32字节; iv = 随机16字节; nonce = 随机16字节hex; ts = 毫秒时间戳
data = BASE64( AES-256-CBC(iv, {"path":"/api/login","body":{...业务参数}}) )
k = BASE64( RSA-OAEP-SHA1(公钥, aesKey) ) ← 私钥解,永不出服务器
mac = HEX( HMAC-SHA256(aesKey, ts+"."+nonce+"."+iv+"."+data) )
POST /api/enc {"k","iv","ts","nonce","mac","data"}
③ 服务器:RSA解封→AES解密→验签→时间戳±5分钟+nonce防重放→内部路由→同信封格式加密响应
④ 客户端验签解密响应
```
- 项目 zip 上传/下载走 TLS 直连(大文件不做信封,`密码` 参数照旧)
- 管理页(admin/xiangmu/lib/settings)在 HTTPS 下照常使用管理密码登录
### 客户端改动清单(C# 参考实现见项目内 `客户端对接示例.cs`)
1. 地址改为 `https://NAS_IP:12701`;信任自签证书(回调跳过或安装证书)
2. 旧 .NET 启用 TLS1.2:`ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072`
3. 登录/注册/查会员/改密/项目云列表/删除 → 走 `EncryptedCall(path, json)` 信封封装
4. 项目上传/下载 → 走 TLS 直连(同 v1.2.0 加 `密码` 参数的方式)
5. 业务字段与响应格式与之前完全一致
### 信封错误响应(明文返回,便于客户端提示)
| 场景 | 响应 |
|---|---|
| 时间戳超出 ±5 分钟 | `{"ok":false,"msg":"时间戳超出允许窗口(±5分钟)"}` |
| 重放同一 nonce | `{"ok":false,"msg":"重复的请求(疑似抓包重放),已拒绝"}` |
| 密文/HMAC 被篡改 | `{"ok":false,"msg":"签名校验失败(数据可能被篡改)"}` |
| 信封字段缺失/损坏 | `{"ok":false,"msg":"信封字段缺失"}` 等 |
## 安全模型(v1.4.0)
- **管理密码**:安装向导必填(sha256+盐存 app.env),登录管理页面与保护 `/api/list`、`/api/save`、`/api/settings`、`/api/admin/changepwd`;token 24h 过期;修改管理密码在设置页"安全"卡片。忘记密码:应用设置(config 向导)重新设置,或重装时在 env 里传 `wizard_admin_pwd`。
- **会员密码哈希存储**(v1.3.0,客户端零改动):存 `盐(32hex)$sha256(64hex)`,每会员独立盐;服务启动自动把存量明文升级为哈希(先备份,成功后用哈希版刷新 .bak)。管理页密码列不显示密码——输入新值即重置,留空保持不变。`/api/save` 密码语义:空=保持、明文=重置、哈希格式=原样。
- **项目云鉴权**:会员密码或管理 token 二选一。
- **限速**:登录/注册/管理登录失败按 IP 限速(1 分钟 10 次锁 60 秒,重启清零)。
- **注册开关**:设置页可关闭开放注册(`HY_REG_OPEN`)。
- **配额与备份**:每会员项目云 1GB;整表保存/迁移前自动 `huiyuan.json.bak`(tmp+rename 原子写,兼容飞牛 ACL 000 权限文件)。
- 已知边界:传输为 HTTP(密码在局域网传输中可见,客户端限制;哈希解决的是数据库文件泄露问题);限速/token 在内存(重启清零)。
## 数据目录解析顺序(数据库 huiyuan.json 与 项目云/ 都在这里)
1. `HY_DB_DIR`(向导指定 / 设置页切换,写入 `TRIM_PKGETC/app.env`)
2. `TRIM_DATA_SHARE_PATHS`(应用数据目录:文件管理 → 应用文件 → fnnas.huiyuan/数据目录)
3. `TRIM_PKGVAR/files`
目录不可写自动降级到下一级;设置页显示实际生效路径。切换目录时自动迁移 `huiyuan.json` + `项目云/`。
## 目录结构
```
fnnas.huiyuan/
├── manifest # service_port=12701, install_dep_apps=nodejs_v22, micro_app=true
├── wizard/install|config # 安装向导 / 装后配置向导(wizard_port、wizard_dbdir)
├── app/server/server.cjs # 主服务(纯 Node http,含项目云 API);首次启动自动建空会员库
├── app/www/ # admin + xiangmu-cloud + lib-cloud + settings + sdk/
├── app/ui/config # 桌面入口,port 用 ${wizard_port} 占位符
├── cmd/ # main(start/stop/status) + install/config 回调(写 app.env)
└── config/ # privilege(run-as package) + resource(data-share + api-scope)
```
## 打包
```bash
cd /vol2/1000/fnosyy
.agents/skills/fnos-fpk-packaging/fnpack-1.2.3-linux-amd64 build -d fnnas.huiyuan
# 注意:打包前确认 app/ 内文件权限位正常(644),源目录复制来的文件可能是 000
```
## NAS 升级流程(install-fpk 不支持原地升级)
```bash
printf 'wizard_port=12701\nwizard_dbdir=/vol2/1000/成套报价软件数据库\n' > /tmp/hy-env
appcenter-cli stop fnnas.huiyuan && appcenter-cli uninstall fnnas.huiyuan
appcenter-cli install-fpk fnnas.huiyuan_X.Y.Z_all.fpk -e /tmp/hy-env
appcenter-cli start fnnas.huiyuan
# 卸载会回收目录授权;数据目录在应用目录外不受影响
```
## 本地测试(模拟飞牛环境)
```bash
HY_PORT=18701 TRIM_PKGETC=/tmp/etc TRIM_PKGVAR=/tmp/var \
TRIM_DATA_SHARE_PATHS=/tmp/share node app/server/server.cjs
```
## 注意
- 客户端(Windows 成套报价软件)连接地址需改为 `飞牛主机IP:端口`(原为 127.0.0.1:12701)
- 项目云接口自 v1.2.0 起需带会员密码(见上方对照表);登录/注册/查会员/改密协议始终不变
- 开放 API(目录授权查询)在非飞牛环境会优雅降级,不影响其他功能
- 项目云存储在数据目录(DB_DIR/项目云),与源码 Windows 版(ROOT/项目云)路径规则不同