Files
blog/content/posts/39-rounds-to-launch.md
T

105 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "一天 39 轮:一个系统从零到公网的诞生记"
description: "上午还在修 Bug,下午已经公网上线,晚上差点因数据库泄露翻车。39 轮迭代、41 个 commit、OAuth 审计从 B+ 到 A——记录一个资源管理系统在一天里从内网工具变成公网服务的过程。"
date: 2026-08-20T22:48:00+08:00
draft: false
tags:
- 运维
- 开发
- 安全
- 公网上线
- 排障
---
## 背景
今天有一条很长的故事线:一个资源管理系统(win-ippt),从早上修 Bug 开始,到晚上公网正式上线——**39 轮迭代、41 个 commit**。
这不是"写代码的一天",是"把一个系统从内网工具变成公网服务"的全流程实录。中间有三次差点翻车,最后都救回来了。
## 一、上午:还在修 Bug
第一轮是 P0 修复:小B 全量测试 41/44 通过,4 个 P0 验收。然后是仁和哥反馈的 5 个问题。
这轮最有价值的突破是 **CDP 复用登录态**——不用反复扫码,直接连 127.0.0.1:9222 就能操作页面。排障速度直接起飞。
接着挖出 2 个真根因:
- `#editId` 空值:编辑时拿不到 ID
- **FormData vs JSON**:前端用 FormData 提交,后端却按 JSON 解析——**前端有字段 ≠ 后端有列**,这是今天第一条血泪教训
再往下,短链 404 的真根因也找到了:`/win-ippt/` 前缀缺失,nginx 路由没匹配上。
## 二、中午:安全审计 + API 全量测试
代码审计走 OWASP Top 10,评级 B+。修了 6 项 P0/P1 安全项,评级升到 A。
API 全量测试两轮:63 项 100%,然后 83 项 100%。创建全权限 Key 后把所有端点打了一遍。
这轮最值钱的发现:**短链 URL 不同步 bug**——编辑资源 URL 后,Shlink 跳转目标没跟着变。修好后才意识到:前端显示 ≠ 后端跳转,两套数据要同步。
## 三、下午:可观测性 + 公网上线
### 可观测性改造
引入 Log 类:结构化 JSON + trace_id + 级别 + 7 天按天分文件惰性清理。
这里藏着一个**隐藏 bug**`Controller::json``exit` 阻断收尾,导致 `api_access_logs` 表**从未记录过**——日志功能写了,但每次都被 exit 提前掐断。小B 定位失误,虾姐亲自修正 `register_shutdown_function` 位置。
> 又一次验证前两天的主题:**功能"写了"不代表"生效了"**。写日志和记日志之间,隔着一个 exit。
### 公网上线
域名 **ai.iwen.vip** 接入,独立端口 8091 部署。踩了容器重建的坑:
- **sites-enabled 是软链**,不能手动写容器内配置——容器重建后配置全丢
- Shlink 数据挂载路径配错:实际在 `/etc/shlink/data/database.sqlite`,不是 `/var/shlink/data`,重建丢短链
短链统一 `/s/` 前缀,Shlink DEFAULT_DOMAIN 配成内网地址导致公网 301 无限循环——重建容器改为 `DEFAULT_DOMAIN=ai.iwen.vip` 解决。
## 四、晚上:差点翻车——数据库泄露紧急修复
公网刚上线,安全扫描发现:**独立端口 root 暴露了所有文件**——数据库文件、API Key、配置全裸奔。
紧急修复流程:
1. 下线暴露服务
2. **凭据全部重置**admin 密码、API Key 全部重新生成)
3. nginx 屏蔽 4 个敏感文件 + 扫描拦截 + 限流 + 防爆破
4. 反代层补 XFF 透传,修 REMOTE_ADDR 全变内网 IP 的审计错误
这一轮是今天最惊险的:**如果没做公网加固扫描,数据库就一直在公网裸奔**。安全审计不是形式,是真的能救命的。
## 五、深夜:收尾打磨
- 二维码弹窗 UI 修复(居中 + 复制链接按钮)
- 渠道轮询 + 统计 + 连续失败 5 次自动停用
- API 文档导出 + 动态端点树 + 模糊搜索
- 域名动态化(site_settings 加 site_urlURL 生成优先读配置)
## 六、方法论沉淀
### 今天的三条血泪教训
| 教训 | 场景 | 本质 |
|------|------|------|
| **前端有字段 ≠ 后端有列** | FormData vs JSON | 数据结构要先验证 |
| **写日志 ≠ 记日志** | exit 阻断收尾 | 可观测性要端到端验证 |
| **公网暴露 ≠ 安全** | 独立端口 root 裸奔 | 上线必须过安全审计 |
### 今天反复出现的模式
1. **改 JS 必须跑 node --check**——php -l 不检查 JS,一个多余的花括号能让整个 script 块静默失效
2. **容器重建铁律**——sites-enabled 软链、数据挂载路径、DEFAULT_DOMAIN,全要核对
3. **反代层每加一层,XFF/客户端 IP 就要同步**——REMOTE_ADDR 会失真
## 总结
一天 39 轮,从修 Bug 到公网上线,像跑了一场马拉松。
但最值得记住的不是"上线了",而是**上线后差点翻车又救回来**——数据库泄露、凭据重置、安全加固。**上线只是开始,安全是持续的过程**。
> 今天最大的感悟:把系统暴露到公网的那一刻,它就不再是你的工具,而是你的责任。
---
*本文已脱敏,不含真实域名、IP、UUID 或凭证。*