Metadata-Version: 2.4
Name: qconf-center
Version: 0.1.1
Summary: Unified configuration center: FastAPI server with web admin and api_key auth, plus a cache-first Python client that only pulls config when its digest changes
Author: qconf contributors
License-Expression: MIT
Keywords: configuration,config-center,settings,fastapi,sqlite
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration
Classifier: Framework :: FastAPI
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24
Requires-Dist: fastapi>=0.100
Requires-Dist: uvicorn[standard]>=0.23
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-timeout>=2.0; extra == "dev"
Dynamic: license-file

# qconf

统一配置中心：FastAPI 服务端（Web 管理界面 + HTTP 同步协议）+ Python 客户端 SDK。
客户端只配 **服务端地址 + 环境名**，按 key 取值；本地落盘缓存，默认每 60 秒带着内容摘要去问一次
「变了吗」，**没变就不传配置内容**，变了才拉全量。

完整设计、实测数据与取舍见 **[DESIGN.md](DESIGN.md)**。

## 一键启动服务端

```bash
# Windows：双击 start_server.bat，或
python run.py                        # http://127.0.0.1:8000/admin/

# 已安装包的场景（PyPI 发布名是 qconf-center，导入名仍是 qconf）
pip install qconf-center             # 服务端 + 客户端依赖一条命令装齐（fastapi/uvicorn/pydantic/httpx）
qconf serve
```

首次打开 `/admin/` 是**三步引导**（欢迎 → 设置管理员账号密码，带强度提示 → 完成页可直达签发 api_key）；管理员存 SQLite（scrypt 哈希）。之后所有 API 都需要凭证：

- **管理员会话**：网页登录后自动持有（12 小时滑动有效期，改密码即全部注销）。
- **api_key（只读）**：在界面右上角「api_key」里签发，给客户端 / CI **拉取配置**用。
  每个 key 只有读权限，并可限定**可访问环境**范围（不选 = 全部环境）；页面表格能看到创建时间、
  最近使用时间、来源 IP、调用次数，可随时停用或删除。完整 key 只在签发时显示一次。
  **任何配置修改都必须用管理员账号在 Web 界面完成**——api_key 请求写接口一律 403。

首次启动只会在 `./qconf-data/qconf.db` 建一个环境：

- **`default`** —— 内置环境，**每次启动自动确保存在、不能删除**，带 3 条基线配置，
  界面里标「内置」徽标且删除按钮禁用，API 删除返回 409。

示例环境**默认不播种**（配置中心把 `DATABASE_URL=postgres://demo…` 这类示例值发出去，正是它要防的事故）：
想要 `dev / test / prod` 演示数据，显式加 `--seed-demo`（或 `QCONF_SEED=1`），且只在库里除内置环境还是空的时候生效。

## 启动配置项

`qconf serve` 的命令行参数（`run.py` 和 `python -m qconf.server` 完全一致）：

| 参数 | 默认 | 说明 |
|---|---|---|
| `--host` | `127.0.0.1` | 绑定地址；`0.0.0.0` 才允许局域网其它机器访问 |
| `--port` | `8000` | 监听端口；被占用时打印原因并以退出码 2 退出 |
| `--db` | `./qconf-data/qconf.db` | SQLite 文件路径，相对启动目录解析 |
| `--seed-demo` | 关 | 播种 dev/test/prod 示例数据（仅在库为空时生效） |
| `--reload` | 关 | 开发模式：代码改动自动重启进程 |
| `--log-level` | `info` | `critical`/`error`/`warning`/`info`/`debug`/`trace` |

等价的环境变量（同一套语义，命令行参数优先级更高；容器 / systemd / nohup 部署时用这些）：

| 环境变量 | 默认 | 说明 |
|---|---|---|
| `QCONF_HOST` / `QCONF_PORT` | `127.0.0.1` / `8000` | 同 `--host` / `--port` |
| `QCONF_DB_PATH` | `./qconf-data/qconf.db` | 同 `--db` |
| `QCONF_SEED` | `0`（关） | `1` / `true` 等价于 `--seed-demo` |
| `QCONF_SESSION_TTL_SECONDS` | `43200`（12h） | 管理员会话有效期，滑动续期；改密码即全部注销 |
| `QCONF_CORS_ORIGINS` | 空 | 逗号分隔的允许跨域来源，别的独立前端要直连 API 时才配 |

## 客户端接入

```bash
pip install qconf-center             # 客户端与服务端同包，装一次即可
```

```python
from qconf import QConf

client = QConf(server="http://127.0.0.1:8000", env="dev", refresh_interval=60)

client.get("DATABASE_URL")            # 只读内存，0.4µs，不阻塞、不联网
client.get("MISSING", default=None)   # 缺省值
client.get_bool("FEATURE_X", False)   # 值都是字符串，提供类型转换
client.as_dict()                      # 完整快照副本
client.close()
```

也可以全靠环境变量，代码里零参数：

```bash
export QCONF_SERVER=http://127.0.0.1:8000
export QCONF_ENV=dev                 # 可省略，默认 default
export QCONF_API_KEY=qk_…            # 在界面「api_key」里签发
export QCONF_REFRESH_INTERVAL=60
```

```python
client = QConf()
```

## 敏感值与回滚

- 界面上每个配置有一个「密/否」开关：标记为敏感后，**管理界面、列表接口、导出文件、变更历史**里都显示 `******`，
  该行同时变成只读（防止把占位符存回真值）。工具栏「敏感值: 显示」需要管理员会话（`?reveal=1`）。
  客户端同步**始终拿到原值** —— 掩码只是管理面行为。
- 标记敏感**不改变配置摘要**，所以不会让任何客户端白拉一次全量。
- 底部「变更历史」面板按 revision 分组显示每个 key 的改前/改后值，任意历史 revision 都有「回滚到此版本之前」按钮：
  回滚后配置摘要与目标 revision 逐字节相同，并生成一个新 revision（回滚自身也可再回滚）。

```bash
# 也可以用 API（写接口只接受管理员会话，请先在界面登录取会话）
curl localhost:8000/api/v1/environments/prod/revisions -H "Authorization: Bearer $QCONF_API_KEY"
# 写接口（改值/回滚/导入/敏感标记）只接受管理员会话，请直接在 Web 界面操作
```

## 命令行

```bash
qconf serve                                          # 启动服务端
qconf --server http://127.0.0.1:8000 --env dev list
qconf --server http://127.0.0.1:8000 --env dev get DATABASE_URL --raw
qconf --server http://127.0.0.1:8000 --env dev status  # 缓存状态 + 一次同步结果
qconf hash local.json                                # 校验摘要算法（跨语言对拍用）
# 没有 put 命令：api_key 只读，配置修改一律在 Web 界面完成
```

## 测试与压测

```bash
pip install -e ".[dev]" && python -m pytest -q    # 92 项：协议/服务端/客户端/鉴权与 api_key/变更历史/真实 HTTP 端到端
python bench/measure_sync.py                      # 延迟、带宽、吞吐、传播时延、冷启动实测
```

## 目录

```
qconf/protocol.py          协议与摘要算法（客户端与服务端共用，纯标准库）
qconf/server/store.py      SQLite 存储：revision / 事务内重算摘要 / 审计
qconf/server/app.py        FastAPI 应用：同步接口 + 管理接口 + 令牌鉴权
qconf/server/static/       零构建 Web 管理界面（原生 HTML/CSS/JS）
qconf/server/__main__.py   一键启动器
qconf/client/core.py       客户端：内存快照 + 摘要门控同步 + 后台轮询线程
qconf/client/cache.py      本地磁盘缓存（原子写、按 服务端+环境 分区）
qconf/cli.py               qconf 命令行
run.py / start_server.bat  免安装的启动入口
client_example.py          接入示例（含变更回调）
bench/measure_sync.py      实测脚本，结果在 bench/results.txt
```
