不想排座位

不想排座位 云同步服务端部署教程

不想排座位 云同步服务端部署教程

老三 · 2026-05-16

之前画的饼来了! 序言,一些有的没的的介绍 - 用户认证 -- 对接老三账户的 OAuth 2.0,一套账号走天下 - 云同步 -- 班级数据的推送和拉取,带冲突检测,不会让你数据打架 - 快照管理 -- 就是云端存档,翻车了能回滚 - 订阅管理 -- 配合老三账户的服务订阅,分了几个等级来控制权限 技术栈就四个字(词?):Django + SQLite + Waitress + PyYAML,够用就行。 --- 一、环境准备 系统要求 - Python 3.10+(低了别来找我) - pip 装依赖 进 backend/ 目录: bash cd backend pip install -r requirements.txt 一共就这么几个包: | 包名 | 干嘛的 | |------|------| | Django | 不解释 | | waitress | 生产用的 WSGI 服务器 | | PyYAML | 读配置文件用的 | | certifi | SSL 证书,OAuth 请求要用 | | tzdata | 时区数据 | --- 二、创建 OAuth 应用 登录要走老三账户,所以你得先去那边注册个应用。 2.1 进服务管理 打开 老三账户开发者平台(点这里),登你的开发者账号。 2.2 创建应用 1. 去 创建新应用 2. 填一下: - 应用名称:FuckSeats 云同步,或者你随便起一个 - 回调地址(Redirect URI):你后端的地址 + /auth/oauth-callback,比如 https://你的域名/auth/oauth-callback 3. 创建完了,把 Client ID 和 Client Secret 记下来 回调地址要跟你配置文件里 server.baseurl + /auth/oauth-callback 完全一样,不然 OAuth 会回调失败。不过说实话你要是不填……可能也没啥事(但别怪我没提醒你)。 --- 三、配置文件 3.1 先复制一份 bash cp cloudconfig.example.yaml cloudconfig.yaml 3.2 改配置 打开 cloudconfig.yaml,照着改: yaml server: 对外访问地址,拼 OAuth 回调用的 生产环境记得改成你的真实域名 baseurl: "https://你的域名" host: "0.0.0.0" port: 8000 生产环境一定要换掉,别用默认的 secretkey: "搞一个又长又随机的字符串放这" debug: false allowedhosts: ["你的域名"] timezone: "Asia/Shanghai" metadata: developername: "老三" developerwebsite: "www.577622.xyz" database: SQLite 数据库路径,相对于 backend/ 目录 name: "cloud.sqlite3" laosanoauth: 从老三账户开发者平台拿到的 clientid: "你的 Client ID" clientsecret: "你的 Client Secret" authorizeurl: "https://www.577622.xyz/oauth/authorize" tokenurl: "https://www.577622.xyz/oauth/token" userinfourl: "https://www.577622.xyz/oauth/userinfo" scope: "email nickname subscriptions" 3.3 几个重点说一下 server 部分 | 字段 | 说明 | 别踩的坑 | |------|------|----------| | baseurl | 对外地址 | 拼回调 URL 用的,必须跟你注册应用时填的一致 | | secretkey | Django 密钥 | 生产环境必须改,建议 50 个字符以上,越乱越好 | | debug | 调试模式 | 上线了就关掉,设成 false | | allowedhosts | 允许的域名 | 看你自己情况填 | laosanoauth 部分 | 字段 | 说明 | |------|------| | clientid | 创建应用后给你的 | | clientsecret | 应用密钥,别泄露了 | | scope | 要 email nickname subscriptions 这三个 | subscription 部分 订阅配置默认给了 free、pro、promax 三档。要对接老三账户的服务订阅的话,每档里要填一个 serviceidentifier,对应你在老三账户那边创建的服务标识符。 | 字段 | 说明 | |------|------| | fallbacktier | 匹配不上任何付费等级时默认用哪个,一般就 free | | tiers.xxx.displayname | 给用户看的等级名字 | | tiers.xxx.serviceidentifier | 老三账户那边的服务标识符,用来匹配用户订阅等级 | | tiers.xxx.price | 展示用的价格,比如 "RMB 9999" | | tiers.xxx.purchaseurl | 购买/管理链接,用户点"订阅"跳去的地址 | | tiers.xxx.limits | 各种限制,班级数量、快照数量、历史步数之类的 | 3.4 环境变量覆盖(可选) 上面那些关键参数都能用环境变量覆盖,Docker 或 CI/CD 的时候比较方便: bash export CLOUDBASEURL="https://你的域名" export CLOUDSECRETKEY="你的密钥" export CLOUDDEBUG="false" export PORT=8000 export LAOSANOAUTHCLIENTID="你的 Client ID" export LAOSANOAUTHCLIENTSECRET="你的 Client Secret" 配置文件路径也能自定义: bash export CLOUDCONFIGPATH="/path/to/your/cloudconfig.yaml" --- 四、启动 4.1 生产模式 bash python run.py 启动后它会自己干这些事:读配置 → 跑数据库迁移 → 用 Waitress 把服务拉起来。 正常的话你会看到: 准备迁移云端数据库... 云端后端启动:http://0.0.0.0:8000 4.2 开发模式 bash python run.py -dev 用 Django 自带的开发服务器跑,改了代码自动重载,调试的时候用这个。 4.3 看看活没活 bash curl http://127.0.0.1:8000/health 返回这个就是正常的: json {"ok": true, "status": "success", "service": "fuckseats-cloud-backend"} --- 五、反向代理(生产环境搞一下) 上线了建议套个 Nginx,顺手把 HTTPS 也配了。 Nginx 配置参考 nginx server { listen 443 ssl; servername 你的域名; sslcertificate /path/to/cert.pem; sslcertificatekey /path/to/key.pem; location / { proxypass http://127.0.0.1:8000; proxysetheader Host $host; proxysetheader X-Real-IP $remoteaddr; proxysetheader X-Forwarded-For $proxyaddxforwardedfor; proxysetheader X-Forwarded-Proto $scheme; } } server { listen 80; servername 你的域名; return 301 https://$host$requesturi; } 配完之后记得把 cloudconfig.yaml 里的 baseurl 改成 https:// 开头的。 --- 六、API 接口 认证相关 | 方法 | 路径 | 干嘛的 | 要不要认证 | |------|------|------|------| | GET | /health | 健康检查 | 不用 | | GET | /auth/login?callback=URL | 发起登录 | 不用 | | GET | /auth/oauth-callback | OAuth 回调,后端自己处理 | 不用 | | POST | /auth/exchange | 拿 sessioncode 换 token | 不用 | | POST | /auth/logout | 登出 | Bearer Token | 用户信息 | 方法 | 路径 | 干嘛的 | 要不要认证 | |------|------|------|------| | GET | /api/me | 看看我是谁 | Bearer Token | | POST | /api/me/refresh-subscription | 刷新订阅状态 | Bearer Token | 云同步 | 方法 | 路径 | 干嘛的 | 要不要认证 | |------|------|------|------| | GET | /api/sync/status | 同步状态 | Bearer Token | | POST | /api/sync/push | 推数据上去 | Bearer Token | | POST | /api/sync/push-batch | 批量推 | Bearer Token | | GET | /api/sync/pull/{uuid} | 把数据拉下来 | Bearer Token | | DELETE | /api/sync/{uuid} | 删掉云端的班级 | Bearer Token | 快照 | 方法 | 路径 | 干嘛的 | 要不要认证 | |------|------|------|------| | GET | /api/snapshots/{uuid} | 看有哪些快照 | Bearer Token | | POST | /api/snapshots | 存一个快照 | Bearer Token | | GET | /api/snapshots/{id}/download | 下载快照 | Bearer Token | | DELETE | /api/snapshots/{id} | 删快照 | Bearer Token | 订阅 | 方法 | 路径 | 干嘛的 | 要不要认证 | |------|------|------|------| | GET | /api/subscription/plans | 看看有哪些订阅计划 | 不用 | | POST | /api/subscription/redeem | 兑换码激活 | Bearer Token | | GET | /api/subscription/purchase-url?tier=xxx | 拿购买链接 | 不用 | --- 七、OAuth 登录流程 整个登录是这么跑的: 客户端 后端 老三账户 | | | |-- GET /auth/login ---------->| | | ?callback=本地回调地址 | | | |-- 302 跳过去 --------------->| | | authorizeurl?clientid=.. | | | | | | | | | 拿授权码换 accesstoken | | | | | |-- GET userinfourl --------->| | | 拿用户信息和订阅 | | | | | | | | {code: sessioncode} | | | | | |<-- sessiontoken + 用户信息 -| | 拿到 token 之后,后面调需要认证的接口都在 Header 里带上: Authorization: Bearer {sessiontoken} --- 八、订阅等级配置 要对接老三账户的订阅功能的话,得先在那边创建服务,然后把标识符填回来。 具体步骤 1. 去 老三账户服务管理 创建服务 2. 把每个服务的 服务标识符 记下来 3. 填到 cloudconfig.yaml 的 subscription.tiers 里 yaml subscription: fallbacktier: "free" tiers: free: displayname: "免费版" limits: maxclassrooms: 3 syncenabled: true maxhistorysteps: 0 syncaiconversations: false maxsnapshotsperclassroom: 3 pro: displayname: "Pro" serviceidentifier: "你在老三账户那边创建的服务标识符" price: "RMB 9999" purchaseurl: "https://www.577622.xyz/manage" # 这个等级单独的购买链接 limits: maxclassrooms: 10 syncenabled: true maxhistorysteps: 10 syncaiconversations: false maxsnapshotsperclassroom: 20 promax: displayname: "Pro Max" serviceidentifier: "另一个服务标识符" price: "看你自己定" purchaseurl: "https://www.577622.xyz/manage" limits: maxclassrooms: -1 # -1 就是不限 # ... 用户登录的时候后端会自动去老三账户的 userinfo 接口拿订阅信息,然后根据 serviceidentifier 匹配等级,不用你手动管。 --- 九、限制和速率控制 这些都能在配置文件里调: yaml ratelimit: syncperminute: 30 # 同步请求,一分钟最多 30 次 authperminuteperip: 10 # 认证请求,一个 IP 一分钟 10 次 snapshotperhour: 20 # 快照操作,一小时 20 次 datalimits: maxpushsizemb: 5 # 单次推送最大 5MB maxbatchpushsizemb: 20 # 批量推送最大 20MB sessiontokenttldays: 7 # Token 7 天过期 sessioncodettlseconds: 60 # 登录换码 60 秒内有效 --- 十、常见问题 OAuth 回调报 "登录状态不存在或已过期" 十有八九是 cloudconfig.yaml 里的 server.baseurl 跟你在老三账户那边填的回调地址对不上。后端拼回调 URL 的规则是 {baseurl}/auth/oauth-callback,检查一下是不是一致的。 启动时报 PyYAML 相关的错 装一下就好了: bash pip install PyYAML 或者设个环境变量,没装 PyYAML 的时候直接报错而不是默默跳过: bash export CLOUDREQUIREYAML=1 怎么生成 secretkey? 跑这个就行: bash python -c "import secrets; print(secrets.tokenurlsafe(50))" 怎么备份数据? 数据库就一个 SQLite 文件,直接把 backend/cloud.sqlite3 拷走就完事了。建议弄个 cron 定时备份。 Docker 部署 dockerfile FROM python:3.12-slim WORKDIR /app COPY backend/ . RUN pip install --no-cache-dir -r requirements.txt EXPOSE 8000 CMD ["python", "run.py"] 跑的时候用环境变量传配置: bash docker run -d \ -p 8000:8000 \ -e CLOUDBASEURL="https://你的域名" \ -e CLOUDSECRETKEY="你的密钥" \ -e LAOSANOAUTHCLIENTID="你的 Client ID" \ -e LAOSANOAUTHCLIENTSECRET="你的 Client Secret" \ -v /data/fuckseats:/app/cloud.sqlite3 \ fuckseats-cloud --- 十一、上线前过一遍 - [ ] Python 3.10+ 装了 - [ ] pip install -r requirements.txt 跑通了 - [ ] 老三账户那边 OAuth 应用创建好了 - [ ] cloudconfig.yaml 填了 Client ID 和 Client Secret - [ ] server.baseurl 跟 OAuth 回调地址对得上 - [ ] server.secretkey 换成随机字符串了 - [ ] server.debug 关了 - [ ] 反向代理配了 - [ ] HTTPS 开了 - [ ] curl /health 返回正常 - [ ] OAuth 登录流程跑通了 --- 每日画饼:1p 和宝塔保姆级教程正在写,马上发