feat: add native fnOS PostgreSQL shared service
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
# fnOS PostgreSQL 共享服务接入指南
|
||||
|
||||
本文面向需要接入 `nxsir.postgresql` 的 fnOS 原生应用。共享服务不依赖 Docker,多个应用共用一个 PostgreSQL 15 进程,但每个应用获得独立数据库、独立登录角色和独立随机密码。
|
||||
|
||||
## 1. 服务约定
|
||||
|
||||
| 项目 | 默认值 |
|
||||
|---|---|
|
||||
| fnOS 应用名 | `nxsir.postgresql` |
|
||||
| PostgreSQL 地址 | `127.0.0.1:15432` |
|
||||
| 管理/API 地址 | `http://127.0.0.1:15433` |
|
||||
| 传输加密 | 不启用 TLS,仅允许本机回环连接 |
|
||||
| 密码认证 | PostgreSQL SCRAM-SHA-256 |
|
||||
| 可申请扩展 | `vector`(pgvector) |
|
||||
|
||||
不要连接 Unix Socket、不要使用服务管理员角色,也不要假设数据库名或角色名。客户端必须通过接入 API 获取完整凭据。
|
||||
|
||||
## 2. 声明 fnOS 依赖
|
||||
|
||||
在应用 FPK 的 `manifest` 中声明:
|
||||
|
||||
```ini
|
||||
install_dep_apps=nxsir.postgresql
|
||||
```
|
||||
|
||||
用户应先安装并启动 PostgreSQL 共享服务,再安装你的应用。你的安装/升级向导需要提供一个密码字段,让用户填写安装共享服务时设置的“应用接入令牌”。令牌长度为 20~256 个字符,不允许换行。
|
||||
|
||||
接入令牌与 PostgreSQL 管理员密码是两套独立凭据:
|
||||
|
||||
- 管理员密码只登录 PostgreSQL 管理面板。
|
||||
- 接入令牌只用于本机应用首次签发或重新签发数据库凭据。
|
||||
|
||||
## 3. 注册客户端
|
||||
|
||||
注册接口只接受来自回环地址的请求:
|
||||
|
||||
```http
|
||||
POST http://127.0.0.1:15433/internal/v1/enroll
|
||||
Authorization: Bearer <应用接入令牌>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
普通 PostgreSQL 客户端:
|
||||
|
||||
```json
|
||||
{
|
||||
"appId": "myapp",
|
||||
"displayName": "My fnOS App",
|
||||
"requestedExtensions": []
|
||||
}
|
||||
```
|
||||
|
||||
需要 pgvector 的应用:
|
||||
|
||||
```json
|
||||
{
|
||||
"appId": "imagefind",
|
||||
"displayName": "ImageFind",
|
||||
"requestedExtensions": ["vector"]
|
||||
}
|
||||
```
|
||||
|
||||
字段限制:
|
||||
|
||||
- `appId`:稳定且全局唯一,3~64 个字符;以小写字母开头,只允许小写字母、数字、点、下划线和连字符。发布后不要更改。
|
||||
- `displayName`:1~100 个字符,用于管理面板展示。
|
||||
- `requestedExtensions`:目前只能是空数组或包含 `vector`。
|
||||
|
||||
curl 示例:
|
||||
|
||||
```bash
|
||||
curl --fail --silent --show-error \
|
||||
-H "Authorization: Bearer $APP_ENROLLMENT_TOKEN" \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{"appId":"myapp","displayName":"My fnOS App","requestedExtensions":[]}' \
|
||||
http://127.0.0.1:15433/internal/v1/enroll
|
||||
```
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"host": "127.0.0.1",
|
||||
"port": 15432,
|
||||
"database": "appdb_myapp_...",
|
||||
"username": "app_myapp_...",
|
||||
"password": "一次性返回的随机密码",
|
||||
"sslMode": "Disable",
|
||||
"serviceVersion": "15"
|
||||
}
|
||||
```
|
||||
|
||||
每次对同一 `appId` 重新注册都会复用其数据库和角色,但会立即轮换密码,使旧密码失效。因此正常启动时应先读取本地凭据,只有首次安装、凭据丢失或明确执行密码轮换时才重新注册。
|
||||
|
||||
常见 HTTP 状态:
|
||||
|
||||
| 状态 | 含义 |
|
||||
|---:|---|
|
||||
| `200` | 注册成功;响应中包含新密码 |
|
||||
| `400` | `appId`、显示名或扩展参数无效 |
|
||||
| `401` | 接入令牌错误或已被管理员轮换 |
|
||||
| `403` | 请求不是从本机回环地址发起 |
|
||||
| `500` | 共享服务内部错误;查看共享服务日志 |
|
||||
|
||||
## 4. fnOS 生命周期脚本建议
|
||||
|
||||
安装回调只负责以 `0600` 保存令牌种子,不要在向导校验阶段依赖网络。应用启动时执行注册,并采用临时文件加原子重命名保存响应。
|
||||
|
||||
建议的持久化文件:
|
||||
|
||||
```text
|
||||
${TRIM_PKGVAR}/postgres-enrollment-token.seed # 首次注册前,0600
|
||||
${TRIM_PKGVAR}/postgres-client.conf # 注册成功后,0600
|
||||
```
|
||||
|
||||
推荐配置格式:
|
||||
|
||||
```ini
|
||||
host=127.0.0.1
|
||||
port=15432
|
||||
database=接口返回值
|
||||
username=接口返回值
|
||||
password=接口返回值
|
||||
```
|
||||
|
||||
注册成功并安全落盘后应删除令牌种子,避免长期保存高权限接入令牌。不要把密码写入日志、命令行参数、进程标题或 Web 前端。应用卸载时是否保留数据库由用户决定;不要自行执行 `DROP DATABASE`。
|
||||
|
||||
服务可能在 NAS 启动时稍晚就绪。建议:
|
||||
|
||||
- 请求超时 10 秒左右。
|
||||
- 每 2 秒重试一次,最多等待 1~2 分钟。
|
||||
- 先探测 `GET /health/ready`,或直接重试注册。
|
||||
- 已有本地凭据时不要因为管理 API 暂时不可用而重新注册;直接尝试 PostgreSQL 连接。
|
||||
|
||||
## 5. 连接字符串
|
||||
|
||||
.NET / Npgsql:
|
||||
|
||||
```text
|
||||
Host=127.0.0.1;Port=15432;Database=<database>;Username=<username>;Password=<password>;SSL Mode=Disable;Timeout=15;Command Timeout=120;Keepalive=30
|
||||
```
|
||||
|
||||
Python / psycopg:
|
||||
|
||||
```python
|
||||
import psycopg
|
||||
|
||||
connection = psycopg.connect(
|
||||
host="127.0.0.1",
|
||||
port=15432,
|
||||
dbname=database,
|
||||
user=username,
|
||||
password=password,
|
||||
sslmode="disable",
|
||||
connect_timeout=15,
|
||||
)
|
||||
```
|
||||
|
||||
JDBC:
|
||||
|
||||
```text
|
||||
jdbc:postgresql://127.0.0.1:15432/<database>?sslmode=disable&connectTimeout=15
|
||||
```
|
||||
|
||||
应用角色是目标数据库及 `public` schema 的所有者,可以执行自身 migrations、建表和创建索引,但不能创建数据库、创建角色、成为超级用户或连接其他托管应用的数据库。
|
||||
|
||||
## 6. pgvector
|
||||
|
||||
注册时申请 `"requestedExtensions":["vector"]` 后,共享服务会在应用数据库内执行:
|
||||
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
```
|
||||
|
||||
应用可直接在 migration 中使用:
|
||||
|
||||
```sql
|
||||
CREATE TABLE embeddings (
|
||||
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
embedding vector(768) NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX embeddings_cosine_idx
|
||||
ON embeddings USING hnsw (embedding vector_cosine_ops);
|
||||
```
|
||||
|
||||
不要由应用尝试安装系统扩展文件。未在允许列表中的扩展会在注册阶段被拒绝。
|
||||
|
||||
## 7. schema 与迁移
|
||||
|
||||
- 新数据库默认撤销 `PUBLIC` 的数据库连接权和 schema 权限。
|
||||
- 应用角色拥有自己的数据库和 `public` schema,可正常运行 Flyway、EF Core、Alembic、Liquibase 等迁移。
|
||||
- 用 custom-format `pg_dump` 迁移旧库时建议使用 `--no-owner --no-acl`。
|
||||
- 恢复前确认目标库为空;不要覆盖其他 `appId` 的数据库。
|
||||
- 切换前至少校验关键表行数,保留旧数据和带 SHA-256 的转储,确认稳定后再人工清理。
|
||||
|
||||
## 8. 密码轮换、吊销与恢复
|
||||
|
||||
管理员可以在 PostgreSQL 管理面板中:
|
||||
|
||||
- 为某个客户端轮换密码:应用必须立即更新本地凭据。
|
||||
- 吊销客户端:对应角色变为 `NOLOGIN`,数据库不会被删除。
|
||||
- 轮换全局接入令牌:旧令牌立即失效,已经签发的数据库密码不受影响。
|
||||
|
||||
应用本地凭据丢失时,让用户在升级/修复向导重新输入当前接入令牌,再用相同 `appId` 注册。数据库内容会保留,但旧数据库密码会失效。
|
||||
|
||||
不要静默回退到陈旧数据库副本。若应用已经完成共享数据库迁移,但凭据不可用,应停止启动并提示修复凭据,避免产生两套分叉数据。
|
||||
|
||||
## 9. 健康检查与排错
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:15433/health
|
||||
curl -fsS http://127.0.0.1:15433/health/ready
|
||||
```
|
||||
|
||||
排查顺序:
|
||||
|
||||
1. 确认 `nxsir.postgresql` 已安装且状态为运行中。
|
||||
2. 确认应用使用 `127.0.0.1`,而不是 NAS 局域网 IP。
|
||||
3. 检查接入 API 端口 `15433` 与数据库端口 `15432` 是否混用。
|
||||
4. 检查凭据文件权限是否为 `0600`,字段是否完整。
|
||||
5. `password authentication failed` 通常表示密码已轮换;用相同 `appId` 重新注册并原子更新凭据。
|
||||
6. `permission denied for database` 通常表示连接了其他应用的数据库;必须使用响应中的 database。
|
||||
7. 共享服务升级或重启不会要求客户端重新注册,应用应使用已有凭据自动重连。
|
||||
|
||||
## 10. 接入验收清单
|
||||
|
||||
- manifest 已声明 `install_dep_apps=nxsir.postgresql`。
|
||||
- 接入 API 只从回环地址调用。
|
||||
- `appId` 固定且不会随版本变化。
|
||||
- 令牌和数据库密码从不写日志。
|
||||
- 凭据以 `0600` 原子落盘,成功后删除令牌种子。
|
||||
- 普通启动不重复注册、不意外轮换密码。
|
||||
- 应用 migrations 能在自己的数据库执行。
|
||||
- 已验证无法连接另一个测试应用的数据库。
|
||||
- 共享服务重启后应用能用原凭据恢复。
|
||||
- 已准备凭据丢失、密码轮换和迁移失败的明确恢复流程。
|
||||
Reference in New Issue
Block a user