Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fccfd775a3 | |||
| 31cabbe044 | |||
| 462adcc424 |
@@ -134,8 +134,8 @@ CONFIG = {
|
|||||||
'history_file': os.path.join(BASE_DIR, '双色球历史数据.xlsx'),
|
'history_file': os.path.join(BASE_DIR, '双色球历史数据.xlsx'),
|
||||||
'lottery_output_dir': os.path.join(BASE_DIR, 'lottery'),
|
'lottery_output_dir': os.path.join(BASE_DIR, 'lottery'),
|
||||||
'records_file': os.path.join(BASE_DIR, '.generation_records.json'),
|
'records_file': os.path.join(BASE_DIR, '.generation_records.json'),
|
||||||
'api_token': os.environ.get('LOTTO_API_TOKEN', 'lotto2026'),
|
'api_token': os.environ.get('LOTTO_API_TOKEN'), # BIZ-108: 强制从 env 读取,删除默认 'lotto2026' 兜底
|
||||||
'auth_enabled': False,
|
'auth_enabled': True, # BIZ-108: 启用鉴权(默认 False 是安全漏洞)
|
||||||
'max_tickets': 1000,
|
'max_tickets': 1000,
|
||||||
'default_tickets': 10,
|
'default_tickets': 10,
|
||||||
# 数据抓取配置(原 web_executor.py 功能)
|
# 数据抓取配置(原 web_executor.py 功能)
|
||||||
@@ -144,6 +144,20 @@ CONFIG = {
|
|||||||
'fetch_timeout': 300, # 抓取超时秒数
|
'fetch_timeout': 300, # 抓取超时秒数
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# BIZ-108 安全加固:启动期强制校验 LOTTO_API_TOKEN
|
||||||
|
# ============================================================
|
||||||
|
# 规则:auth_enabled=True 时,token 必须存在且长度 ≥ 32
|
||||||
|
# 缺失或过短 → 启动失败(fail-fast),避免静默使用默认 token
|
||||||
|
_API_TOKEN = CONFIG['api_token']
|
||||||
|
if CONFIG['auth_enabled']:
|
||||||
|
if not _API_TOKEN or len(_API_TOKEN) < 32:
|
||||||
|
raise RuntimeError(
|
||||||
|
"[BIZ-108] LOTTO_API_TOKEN 缺失或长度 < 32 字符。"
|
||||||
|
"请在 /etc/lotto/env 配置强随机 token(建议 64 字符)。"
|
||||||
|
"生成命令: openssl rand -base64 48 | tr -d '\\n=' | cut -c1-64"
|
||||||
|
)
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 生成记录管理(线程安全)
|
# 生成记录管理(线程安全)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -291,9 +305,20 @@ def find_draw_by_gen_time(gen_time):
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
# 认证装饰器(可选)
|
# 认证装饰器(可选)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
# BIZ-109 临时放行白名单:仅 GET 只读接口临时放行鉴权(应急修复)
|
||||||
|
# 原因:BIZ-108 启用 auth_enabled 后,index.html 前端未适配 token 注入,
|
||||||
|
# 导致「记录」tab 完全不可用。临时放行只读接口恢复业务,
|
||||||
|
# BIZ-110 跟踪 index.html 适配后恢复鉴权。
|
||||||
|
# 白名单接口:GET /api/records, GET /api/history
|
||||||
|
# 保留鉴权接口:所有 POST/DELETE/写接口
|
||||||
|
AUTH_BYPASS_PATHS = {'/api/records', '/api/history'}
|
||||||
|
|
||||||
def require_auth(f):
|
def require_auth(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated(*args, **kwargs):
|
def decorated(*args, **kwargs):
|
||||||
|
# BIZ-109 应急:GET 只读接口临时放行
|
||||||
|
if request.method == 'GET' and request.path in AUTH_BYPASS_PATHS:
|
||||||
|
return f(*args, **kwargs)
|
||||||
if CONFIG['auth_enabled']:
|
if CONFIG['auth_enabled']:
|
||||||
token = request.headers.get('Authorization', '').replace('Bearer ', '')
|
token = request.headers.get('Authorization', '').replace('Bearer ', '')
|
||||||
if token != CONFIG['api_token']:
|
if token != CONFIG['api_token']:
|
||||||
|
|||||||
@@ -224,3 +224,83 @@ curl http://127.0.0.1:8085/api/status
|
|||||||
---
|
---
|
||||||
|
|
||||||
> 部署人:严维序 (opengineer) | 2026-07-04
|
> 部署人:严维序 (opengineer) | 2026-07-04
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BIZ-105 部署记录 — 2026-08-31
|
||||||
|
|
||||||
|
| 项 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 部署时间 | 2026-08-31 05:30 (Asia/Shanghai) |
|
||||||
|
| 部署人员 | 严维序 (opengineer) |
|
||||||
|
| 任务来源 | COO 陆怀瑾 sessions_send(父 BIZ-106 子任务3) |
|
||||||
|
| 代码版本 | commit 5d459b450525063ecdbbfb5694aff30aba5fb0bf |
|
||||||
|
| 分支 | biz-105-impl-prize-display |
|
||||||
|
| PRD | BIZ-102 v1.1 + 中奖金额展示 v1.2 §4.3 |
|
||||||
|
| 改动范围 | app.py +110 / index.html +89 / 新增 tests/test_compare_prize.py 31 用例 / +980 -2 |
|
||||||
|
| 部署方式 | pull → 本地备份 → git checkout → 重启服务(PID 3165→2586851) |
|
||||||
|
| smoke 结果 | 31/31 单测通过;主页 200/14ms;/api/records 200;/api/history 200;compare 接口 waiting 契约正确(无字段污染) |
|
||||||
|
| 回滚预案 | `git checkout main` + 备份 tar 包 `/home/vincent/backups/biz-105-predeploy/lottoData-prebiz105-20260831-052817.tar.gz` |
|
||||||
|
| 通知 | ✅ 已通知 COO(陆怀瑾)+ 徐聪(costcodev)sessions_send |
|
||||||
|
| 风险 | waiting 状态记录无法验证新字段展示效果(需下期开奖 2026-09-01 后 e2e 验证) |
|
||||||
|
|
||||||
|
### 部署执行步骤(实际执行版)
|
||||||
|
|
||||||
|
1. **备份**:tar 打包 app.py/index.html/lottery.py/... → `/home/vincent/backups/biz-105-predeploy/`
|
||||||
|
2. **stash 本地修改**:`git stash push -m "biz-105-predeploy-local-changes-20260831"`
|
||||||
|
3. **checkout 分支**:`git checkout biz-105-impl-prize-display`(HEAD = 5d459b4)
|
||||||
|
4. **停止服务**:`kill 3165`(SIGTERM 优雅停止,3s 内)
|
||||||
|
5. **启动新版本**:`nohup ./.venv/bin/python3 ./app.py > /tmp/lotto-biz105-YYYYMMDD-HHMMSS.log 2>&1 &`
|
||||||
|
6. **smoke 验证**:31 单测 + HTTP 200 三接口 + waiting 契约
|
||||||
|
7. **持久化确认**:PID 2586851 监听 8085,日志无异常
|
||||||
|
|
||||||
|
### 已知后续验证项
|
||||||
|
|
||||||
|
- [ ] 2026-09-01 开奖后,对历史生成记录做 e2e 中奖金额展示验证
|
||||||
|
- [ ] 浮动奖(一/二等奖)橙色提示条视觉验证
|
||||||
|
- [ ] 总中奖金额角标位置/样式验证
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## BIZ-105 部署后 Smoke 报告(COO 补充要求)— 2026-08-31 06:00
|
||||||
|
|
||||||
|
按 COO 陆怀瑾 sessions_send 补充要求,完成 5a/5b/5c 三项 curl smoke:
|
||||||
|
|
||||||
|
### 5a 接口层 smoke
|
||||||
|
- **waiting 路径**:5 字段严格保持,无新字段污染 ✅
|
||||||
|
- **compared 路径**:13 字段齐全 + BIZ-105 5 新字段格式正确 ✅
|
||||||
|
- `total_prize: 5`、`total_prize_display: '5元'`
|
||||||
|
- `has_variable_prize: False`
|
||||||
|
- `prize_summary: {sixth: {count: 1, amount: 5}}`
|
||||||
|
- `prize_display: [{level: 六等奖, count: 1, amount: '5元'}]`
|
||||||
|
- **真实数据样本**:record f774a886(5 注),1 注中六等奖(5 元)
|
||||||
|
|
||||||
|
### 5b 浮动奖逻辑 smoke(4 场景全过)
|
||||||
|
| 场景 | 输入 | 预期 | 实际 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 仅固定奖 | 三(2)+四(1)+五(2)+六(1) | total=6225, has_var=False | ✅ |
|
||||||
|
| 含一等奖 | 一(1)+三(2)+六(1) | total=6005(不含一), first.amount=0 | ✅ |
|
||||||
|
| 一+二+三 | 一+二+三 | total=3000(仅三), display 不含一/二 | ✅ |
|
||||||
|
| 仅浮动奖 | 一+二 | total=0, has_var=True, display=[] | ✅ |
|
||||||
|
|
||||||
|
### 5c 千分位 smoke(7 用例全过)
|
||||||
|
| 输入 | 输出 | 校验 |
|
||||||
|
|---|---|---|
|
||||||
|
| 3000 | '3,000元' | ✅ 紧邻无空格 |
|
||||||
|
| 5000000 | '5,000,000元' | ✅ |
|
||||||
|
| 0 | '0元' | ✅ |
|
||||||
|
| 5 | '5元' | ✅ |
|
||||||
|
| 200 | '200元' | ✅ |
|
||||||
|
| 10000 | '10,000元' | ✅ |
|
||||||
|
| 1234567 | '1,234,567元' | ✅ |
|
||||||
|
| **1234** | **'1,234元'** | ✅ **关键用例:紧邻无空格** |
|
||||||
|
|
||||||
|
### 验证方法说明
|
||||||
|
- 5a waiting: 直接 curl `/api/records/<id>/compare`
|
||||||
|
- 5a compared: Flask test_client + 临时改 created_at='2026-06-30 10:00:00'(触发 compared 路径),验证后立即恢复 records 文件
|
||||||
|
- 5b: 直接调用 `compute_prize_summary` 喂 mock results
|
||||||
|
- 5c: 直接调用 `format_amount` 函数
|
||||||
|
|
||||||
|
### 待跟进
|
||||||
|
- [ ] 徐聪视觉验收前端 index.html(橙色浮动奖角标 + badge + 汇总表样式)
|
||||||
|
- [ ] 2026-09-01 开奖后真实数据 e2e(含真实中奖金额展示)
|
||||||
|
|||||||
Reference in New Issue
Block a user