Compare commits

..

29 Commits

Author SHA1 Message Date
shanshanzhong147 e22a34ef3e build: 优化 Dockerfile 构建缓存并移除 API 解密中间件调试日志
Build docker and publish / prepare (20.15.1) (push) Successful in 12s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 1m30s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 1m55s
Build docker and publish / notify (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m13s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 5m30s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 1m36s
Build docker and publish / deploy (push) Successful in 44s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 1m32s
2026-03-01 20:30:28 -08:00
shanshanzhong147 b8dab70de5 feat: 新增设备登录开关配置,优化 CI Go 模块缓存机制,并添加解密中间件调试日志。
Build docker and publish / prepare (20.15.1) (push) Successful in 10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 1m16s
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Has been cancelled
2026-03-01 20:21:02 -08:00
shanshanzhong147 d3da56891b chore: 更新 api-dev.yaml 以硬编码应用密钥,并优化部署工作流,增加 Go 模块缓存并启用 Docker 构建缓存。
Build docker and publish / prepare (20.15.1) (push) Successful in 10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 8m45s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 10m59s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Has been cancelled
2026-03-01 19:44:36 -08:00
shanshanzhong147 3b4429bdd9 feat: 新增设备登录功能,包括API接口、RPC服务、逻辑处理和相关数据类型及错误码。
Build docker and publish / prepare (20.15.1) (push) Successful in 11s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m32s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 8m6s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m26s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 8m23s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 4m1s
Build docker and publish / deploy (push) Successful in 45s
Build docker and publish / notify (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 3m55s
2026-03-01 18:51:12 -08:00
shanshanzhong147 be4cc669d2 env 更新
Build docker and publish / prepare (20.15.1) (push) Successful in 10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m16s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 7m46s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m24s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 4m9s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 3m52s
Build docker and publish / deploy (push) Successful in 37s
Build docker and publish / notify (push) Successful in 4s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 8m14s
2026-02-28 05:32:49 -08:00
shanshanzhong147 54d4ebd54c 配置信息
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 8m6s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 4m7s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m11s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m11s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Successful in 8m12s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 4m9s
Build docker and publish / deploy (push) Successful in 41s
Build docker and publish / notify (push) Successful in 2s
2026-02-28 04:31:20 -08:00
shanshanzhong147 20c87d79ea core rpc
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.rpc-core image_name:ppanel-rpc-core name:rpc-core]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / prepare (20.15.1) (push) Successful in 7s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Has been cancelled
2026-02-28 04:23:03 -08:00
shanshanzhong147 b42c90188a go mod tidy
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Has been cancelled
2026-02-28 04:15:05 -08:00
shanshanzhong147 9bdc593db4 环境变量
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Failing after 1m37s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Failing after 1m42s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Failing after 1m42s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Failing after 1m34s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Failing after 1m39s
Build docker and publish / deploy (push) Has been skipped
Build docker and publish / notify (push) Successful in 2s
2026-02-28 04:02:14 -08:00
shanshanzhong147 baeb875471 dockerfile
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Successful in 7m56s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Successful in 4m12s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Successful in 3m47s
Build docker and publish / deploy (push) Successful in 36s
Build docker and publish / notify (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Successful in 4m5s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Successful in 3m49s
2026-02-28 02:35:11 -08:00
shanshanzhong147 f6047c6476 dockerfile
Build docker and publish / prepare (20.15.1) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Has been cancelled
2026-02-28 02:26:18 -08:00
shanshanzhong147 a67a889293 dockerfile
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Successful in 8m0s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Successful in 4m4s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Successful in 3m51s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Successful in 3m53s
Build docker and publish / deploy (push) Failing after 17s
Build docker and publish / notify (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Successful in 4m23s
2026-02-28 01:54:14 -08:00
shanshanzhong147 1cd83a3099 dockerfile
Build docker and publish / prepare (20.15.1) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Has been cancelled
2026-02-28 01:44:32 -08:00
shanshanzhong147 e8c651cc6b dockerfile
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 1m40s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Successful in 4m11s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Successful in 4m6s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Successful in 3m53s
Build docker and publish / notify (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Successful in 4m12s
Build docker and publish / deploy (push) Has been skipped
2026-02-27 23:48:18 -08:00
shanshanzhong147 24c2cbd0e0 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Successful in 3m58s
Build docker and publish / deploy (push) Has been skipped
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 1m41s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Successful in 4m7s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Successful in 3m35s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Successful in 3m45s
Build docker and publish / notify (push) Successful in 2s
2026-02-27 23:30:41 -08:00
shanshanzhong147 5af84c5628 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Has been cancelled
2026-02-27 23:24:38 -08:00
shanshanzhong147 7750daed54 修复gitea文件
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / prepare (20.15.1) (push) Has been cancelled
2026-02-27 23:20:54 -08:00
shanshanzhong147 6363a85505 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 5s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Successful in 1m12s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Successful in 1m7s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Successful in 1m10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Successful in 1m7s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Successful in 1m10s
Build docker and publish / deploy (push) Failing after 19s
Build docker and publish / notify (push) Failing after 3s
2026-02-27 22:02:15 -08:00
shanshanzhong147 552bfb0915 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Successful in 1m8s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Successful in 1m10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Successful in 1m10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Successful in 1m7s
Build docker and publish / deploy (push) Failing after 1s
Build docker and publish / notify (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Successful in 1m12s
2026-02-27 21:53:37 -08:00
shanshanzhong147 18ee00a23f 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Successful in 1m8s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Successful in 1m6s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Successful in 1m10s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Successful in 1m7s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Successful in 1m7s
Build docker and publish / deploy (push) Failing after 3s
Build docker and publish / notify (push) Failing after 2s
2026-02-27 21:46:34 -08:00
shanshanzhong147 72c11fc762 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
2026-02-27 21:17:45 -08:00
shanshanzhong147 329a7815b8 修复gitea文件
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Failing after 1m18s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 1m15s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Failing after 1m16s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Failing after 1m20s
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Failing after 1m18s
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
2026-02-27 21:14:31 -08:00
shanshanzhong147 2161df8cc7 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 1s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Failing after 1s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Failing after 1s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Failing after 1s
Build docker and publish / deploy (push) Has been cancelled
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Failing after 1s
2026-02-27 21:13:19 -08:00
shanshanzhong147 8d1c16ba04 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Failing after 1m39s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 35s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Failing after 35s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Failing after 35s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Failing after 35s
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
2026-02-27 21:07:31 -08:00
shanshanzhong147 dae845fe74 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Failing after 1m4s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Failing after 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Failing after 4s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Failing after 3s
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
2026-02-27 18:49:09 -08:00
shanshanzhong147 77ab4e4785 修复gitea文件
Build docker and publish / prepare (20.15.1) (push) Successful in 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Failing after 3s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Failing after 2s
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
2026-02-27 18:43:45 -08:00
shanshanzhong147 fe914e781e 修复gitea文件
Build docker and publish / prepare (push) Successful in 0s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:zero-ppanel-admin name:admin]) (push) Failing after 15s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:zero-ppanel-api name:api]) (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:zero-ppanel-node name:node]) (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:zero-ppanel-queue name:queue]) (push) Failing after 2s
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:zero-ppanel-scheduler name:scheduler]) (push) Failing after 2s
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
2026-02-27 17:34:26 -08:00
shanshanzhong147 cd7be35668 feat: Add .claude.json to .gitignore
Build docker and publish / notify (push) Has been cancelled
Build docker and publish / prepare (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.admin image_name:ppanel-admin name:admin]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.api image_name:ppanel-api name:api]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.node image_name:ppanel-node name:node]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.queue image_name:ppanel-queue name:queue]) (push) Has been cancelled
Build docker and publish / build (map[dockerfile:deploy/Dockerfile.scheduler image_name:ppanel-scheduler name:scheduler]) (push) Has been cancelled
Build docker and publish / deploy (push) Has been cancelled
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-27 08:57:00 -08:00
shanshanzhong147 e1896f677c feat: 引入签名认证、加密工具包及大量goctl代码生成模板,并更新API、Admin和Node服务逻辑。 2026-02-27 08:49:37 -08:00
204 changed files with 5496 additions and 700 deletions
+12
View File
@@ -0,0 +1,12 @@
# 数据库连接字符串
# 请根据您的实际环境修改此处的数据库用户名、密码、地址、端口和数据库名
DATABASE_DSN="mysql://root:password@tcp(127.0.0.1:3306)/ppanel?charset=utf8mb4&parseTime=true&multiStatements=true"
# 应用签名密钥 (App Signature Secrets)
# 在 Go-Zero 配置 (e.g., apps/api/etc/api-dev.yaml) 中,
# AppSecrets 下的键名 (例如 "android-client", "web-client") 即为 APP_ID。
# 对应的环境变量值 (APP_SECRET_ANDROID_CLIENT, APP_SECRET_WEB_CLIENT) 为其 SECRET_KEY。
APP_SECRET_ANDROID_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "android-client"
APP_SECRET_WEB_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "web-client"
APP_SECRET_IOS_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "ios-client"
APP_SECRET_MAC_CLIENT="uB4G,XxL2{7b" # 对应 APP_ID "mac-client"
+176 -52
View File
@@ -17,8 +17,8 @@ env:
SSH_PORT: ${{ vars.SSH_PORT }}
SSH_USER: ${{ vars.SSH_USER }}
SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }}
TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }}
TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}
TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0
TG_CHAT_ID: "-4940243803"
VERSION: ${{ github.sha }}
BUILDTIME: ${{ github.event.head_commit.timestamp }}
@@ -27,11 +27,20 @@ jobs:
# Job 1: 设置环境变量,供后续 jobs 共享
# ============================================================
prepare:
runs-on: ario-server
runs-on: zero-ppanel-server
container:
image: node:20
strategy:
matrix:
# 只有node支持版本号别名
node: ['20.15.1']
outputs:
docker_tag: ${{ steps.vars.outputs.docker_tag }}
container_suffix: ${{ steps.vars.outputs.container_suffix }}
deploy_path: ${{ steps.vars.outputs.deploy_path }}
has_changes: ${{ steps.changes.outputs.has_changes }}
changed_services: ${{ steps.changes.outputs.changed_services }}
deploy_services: ${{ steps.changes.outputs.deploy_services }}
steps:
- name: ⚙️ 计算部署变量
id: vars
@@ -54,16 +63,127 @@ jobs:
;;
esac
- name: 📥 下载代码
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🔍 检测变更服务
id: changes
shell: bash
run: |
set -euo pipefail
BEFORE="${{ github.event.before }}"
SHA="${{ github.sha }}"
RANGE=""
if [ -n "${BEFORE}" ] && [ "${BEFORE}" != "0000000000000000000000000000000000000000" ]; then
RANGE="${BEFORE}..${SHA}"
elif git rev-parse HEAD~1 >/dev/null 2>&1; then
RANGE="HEAD~1..HEAD"
else
RANGE="HEAD"
fi
if [ "${RANGE}" = "HEAD" ]; then
git show --pretty=format: --name-only HEAD | sed '/^$/d' > changed_files.txt
else
git diff --name-only "${RANGE}" > changed_files.txt
fi
echo "Changed files:"
cat changed_files.txt || true
has_file() {
grep -Eq "$1" changed_files.txt
}
add_service() {
local service="$1"
if [[ " ${services[*]} " != *" ${service} "* ]]; then
services+=("${service}")
fi
}
all=0
if has_file '^(go\.mod|go\.sum|pkg/|sql/|deploy/docker-compose\.cloud\.yml|deploy/docker-compose-env\.yml|deploy/\.env\.example|\.gitea/workflows/deploy\.yml)'; then
all=1
fi
services=()
if [ "${all}" -eq 1 ]; then
services=(rpc-core api admin node queue scheduler)
else
if has_file '^apps/rpc/' || has_file '^deploy/Dockerfile.rpc-core$' || has_file '^deploy/etc/core/'; then
add_service "rpc-core"
fi
if has_file '^apps/api/' || has_file '^deploy/Dockerfile.api$' || has_file '^deploy/etc/api/'; then
add_service "api"
fi
if has_file '^apps/admin/' || has_file '^deploy/Dockerfile.admin$' || has_file '^deploy/etc/admin/'; then
add_service "admin"
fi
if has_file '^apps/node/' || has_file '^deploy/Dockerfile.node$' || has_file '^deploy/etc/node/'; then
add_service "node"
fi
if has_file '^apps/queue/' || has_file '^deploy/Dockerfile.queue$' || has_file '^deploy/etc/queue/'; then
add_service "queue"
fi
if has_file '^apps/scheduler/' || has_file '^deploy/Dockerfile.scheduler$' || has_file '^deploy/etc/scheduler/'; then
add_service "scheduler"
fi
fi
if [ "${#services[@]}" -eq 0 ]; then
echo "No service changes detected, skip build/deploy."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
echo "changed_services=" >> "$GITHUB_OUTPUT"
echo "deploy_services=" >> "$GITHUB_OUTPUT"
exit 0
fi
deploy_services=()
for service in "${services[@]}"; do
case "${service}" in
rpc-core) deploy_services+=("ppanel-rpc-core") ;;
api) deploy_services+=("ppanel-api") ;;
admin) deploy_services+=("ppanel-admin") ;;
node) deploy_services+=("ppanel-node") ;;
queue) deploy_services+=("ppanel-queue") ;;
scheduler) deploy_services+=("ppanel-scheduler") ;;
esac
done
changed_services="$(IFS=,; echo "${services[*]}")"
deploy_services_str="${deploy_services[*]}"
echo "has_changes=true" >> "$GITHUB_OUTPUT"
echo "changed_services=${changed_services}" >> "$GITHUB_OUTPUT"
echo "deploy_services=${deploy_services_str}" >> "$GITHUB_OUTPUT"
echo "Will build services: ${changed_services}"
echo "Will deploy services: ${deploy_services_str}"
# ============================================================
# Job 2: 并行矩阵构建 5 个服务镜像
# Job 2: 并行矩阵构建 6 个服务镜像 - 💥 重点修改这里 💥
# ============================================================
build:
runs-on: ario-server
runs-on: zero-ppanel-server
container: # <-- 整个 build job 在 Node.js 容器中运行
image: node:20.15.1
volumes:
- /root/go/pkg/mod:/root/go/pkg/mod
- /root/.cache/go-build:/root/.cache/go-build
needs: prepare
if: needs.prepare.outputs.has_changes == 'true' && contains(needs.prepare.outputs.changed_services, matrix.service.name)
strategy:
fail-fast: false
matrix:
service:
- name: rpc-core
dockerfile: deploy/Dockerfile.rpc-core
image_name: ppanel-rpc-core
- name: api
dockerfile: deploy/Dockerfile.api
image_name: ppanel-api
@@ -84,65 +204,61 @@ jobs:
- name: 📥 下载代码
uses: actions/checkout@v4
- name: 🔧 安装 Docker CLI
- name: Set up Go environment # 在 build job 中也设置 Go 环境
uses: actions/setup-go@v2
with:
go-version: '1.24.0' # 确保使用 go.mod 中指定的精确版本
- name: 🔧 确保 Docker CLI 可用并初始化 Go Modules
run: |
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y ca-certificates curl gnupg
# 等待 apt 锁释放
for i in $(seq 1 60); do
if ! fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; then break; fi
echo "等待 apt 锁... ($i/60)"; sleep 5
done
apt-get update -y -o Dpkg::Lock::Timeout=300
apt-get install -y -o Dpkg::Lock::Timeout=300 ca-certificates curl gnupg
# 安装 Docker 官方 CLI (API >= 1.44)
install -m 0755 -d /etc/apt/keyrings
# !!! 在 node 容器中安装 docker-ce-cli !!!
curl -fsSL https://download.docker.com/linux/debian/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -y -o Dpkg::Lock::Timeout=300
apt-get install -y -o Dpkg::Lock::Timeout=300 docker-ce-cli docker-buildx-plugin
apt-get update -y
apt-get install -y docker-ce-cli # <<-- 确保 docker CLI 被安装
echo "Docker CLI 版本: $(docker --version)"
echo "API 版本: $(docker version --format '{{.Client.APIVersion}}')"
echo "Test Docker connectivity: $(docker info --format '{{.ServerVersion}}') (Server)"
- name: 📤 构建并推送 ${{ matrix.service.name }}
go env -w GOPROXY=https://goproxy.cn,direct # 设置 Go Proxy
go mod download # 确保所有模块已下载(缓存命中时自动跳过)
- name: 📦 构建并推送 Docker 镜像
run: |
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
IMAGE_BASE="${{ env.REPO }}/${{ matrix.service.image_name }}"
set -e
IMAGE_NAME="${{ matrix.service.image_name }}"
DOCKERFILE="${{ matrix.service.dockerfile }}"
DEPLOY_TAG="${{ needs.prepare.outputs.docker_tag }}"
REPO="${{ env.REPO }}"
echo "构建服务: ${{ matrix.service.name }}"
echo "镜像: ${IMAGE_BASE}"
echo "标签: ${DOCKER_TAG} | ${{ env.VERSION }}"
FULL_IMAGE_NAME="${REPO}/${IMAGE_NAME}:${DEPLOY_TAG}"
docker build \
-f ${{ matrix.service.dockerfile }} \
--platform linux/amd64 \
--build-arg VERSION=${{ env.VERSION }} \
--build-arg BUILDTIME=${{ env.BUILDTIME }} \
-t ${IMAGE_BASE}:${{ env.VERSION }} \
-t ${IMAGE_BASE}:${DOCKER_TAG} \
.
echo "🚀 开始构建镜像: ${FULL_IMAGE_NAME} 从 ${DOCKERFILE}"
docker build -t "${FULL_IMAGE_NAME}" -f "${DOCKERFILE}" .
docker push ${IMAGE_BASE}:${{ env.VERSION }}
docker push ${IMAGE_BASE}:${DOCKER_TAG}
echo "⬆️ 推送镜像: ${FULL_IMAGE_NAME}"
docker push "${FULL_IMAGE_NAME}"
echo "✅ ${{ matrix.service.name }} 镜像推送完成"
# ============================================================
# Job 3: 部署到服务器
# ============================================================
deploy:
runs-on: ario-server
runs-on: zero-ppanel-server
container: # <-- 新增:deploy job 也在 Node.js 容器中运行
image: node:20.15.1
needs: [prepare, build]
# PR 不触发部署,只有直接推送才部署
if: github.event_name == 'push'
if: github.event_name == 'push' && needs.prepare.outputs.has_changes == 'true'
steps:
- name: 📥 下载代码 (获取 docker-compose.cloud.yml)
@@ -155,7 +271,7 @@ jobs:
username: ${{ env.SSH_USER }}
password: ${{ env.SSH_PASSWORD }}
port: ${{ env.SSH_PORT }}
source: "deploy/docker-compose.cloud.yml"
source: "deploy/docker-compose.cloud.yml,deploy/etc,deploy/.env.example"
target: "${{ needs.prepare.outputs.deploy_path }}/"
strip_components: 1
@@ -173,24 +289,32 @@ jobs:
DEPLOY_PATH="${{ needs.prepare.outputs.deploy_path }}"
DOCKER_TAG="${{ needs.prepare.outputs.docker_tag }}"
REPO="${{ env.REPO }}"
DEPLOY_SERVICES="${{ needs.prepare.outputs.deploy_services }}"
echo "部署目录: ${DEPLOY_PATH}"
echo "镜像标签: ${DOCKER_TAG}"
cd ${DEPLOY_PATH}
# 写入环境变量供 docker-compose 使用
cat > .env <<EOF
PPANEL_TAG=${DOCKER_TAG}
PPANEL_REPO=${REPO}
EOF
# 保留已有业务配置,仅更新镜像仓库和标签
touch .env
grep -q '^PPANEL_TAG=' .env \
&& sed -i "s|^PPANEL_TAG=.*|PPANEL_TAG=${DOCKER_TAG}|" .env \
|| echo "PPANEL_TAG=${DOCKER_TAG}" >> .env
grep -q '^PPANEL_REPO=' .env \
&& sed -i "s|^PPANEL_REPO=.*|PPANEL_REPO=${REPO}|" .env \
|| echo "PPANEL_REPO=${REPO}" >> .env
if [ -z "${DEPLOY_SERVICES}" ]; then
echo "没有服务变更,跳过部署。"
exit 0
fi
# 拉取所有服务的最新镜像
docker-compose -f docker-compose.cloud.yml pull
docker-compose -f docker-compose.cloud.yml pull ${DEPLOY_SERVICES}
# 滚动更新所有 ppanel 服务
docker-compose -f docker-compose.cloud.yml up -d \
ppanel-api ppanel-admin ppanel-node ppanel-queue ppanel-scheduler
docker-compose -f docker-compose.cloud.yml up -d ${DEPLOY_SERVICES}
# 清理旧镜像
docker image prune -f || true
@@ -202,7 +326,7 @@ jobs:
# Job 4: 通知
# ============================================================
notify:
runs-on: ario-server
runs-on: zero-ppanel-server
needs: [prepare, build, deploy]
if: always() && github.event_name == 'push'
steps:
@@ -212,7 +336,7 @@ jobs:
token: ${{ env.TG_BOT_TOKEN }}
to: ${{ env.TG_CHAT_ID }}
message: |
${{ (needs.build.result == 'success' && needs.deploy.result == 'success') && '✅ 部署成功!' || '❌ 部署失败!' }}
${{ needs.prepare.outputs.has_changes != 'true' && '⏭️ 无服务变更,已跳过构建与部署。' || ((needs.build.result == 'success' && needs.deploy.result == 'success') && '✅ 部署成功!' || '❌ 部署失败!') }}
📦 项目: zero-ppanel
🌿 分支: ${{ github.ref_name }}
@@ -221,6 +345,6 @@ jobs:
👤 提交者: ${{ github.actor }}
🕐 时间: ${{ github.event.head_commit.timestamp }}
构建: ${{ needs.build.result }} | 部署: ${{ needs.deploy.result }}
${{ (needs.build.result != 'success' || needs.deploy.result != 'success') && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
构建: ${{ needs.prepare.outputs.has_changes != 'true' && 'skipped(no changes)' || needs.build.result }} | 部署: ${{ needs.prepare.outputs.has_changes != 'true' && 'skipped(no changes)' || needs.deploy.result }}
${{ (needs.prepare.outputs.has_changes == 'true' && (needs.build.result != 'success' || needs.deploy.result != 'success')) && '⚠️ 请检查 Actions 日志获取详细信息' || '' }}
parse_mode: Markdown
+3
View File
@@ -14,6 +14,8 @@ logs/
# Local config overrides
etc/*.local.yaml
apps/*/etc/*.local.yaml
deploy/.env
goctl_tpl/
# IDE
.idea/
@@ -34,3 +36,4 @@ tmp/
# Claude
CLAUDE.md
.claude/
.claude.json
View File
+1
View File
@@ -0,0 +1 @@
/cache
+126
View File
@@ -0,0 +1,126 @@
# the name by which the project can be referenced within Serena
project_name: "zero-ppanel"
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp
# csharp_omnisharp dart elixir elm erlang
# fortran fsharp go groovy haskell
# java julia kotlin lua markdown
# matlab nix pascal perl php
# php_phpactor powershell python python_jedi r
# rego ruby ruby_solargraph rust scala
# swift terraform toml typescript typescript_vts
# vue yaml zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- go
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# The language backend to use for this project.
# If not set, the global setting from serena_config.yml is used.
# Valid values: LSP, JetBrains
# Note: the backend is fixed at startup. If a project with a different backend
# is activated post-init, an error will be returned.
language_backend:
# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true
# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
included_optional_tools: []
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
fixed_tools: []
# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:
# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# This setting can, in turn, be overridden by CLI parameters (--mode).
default_modes:
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
# time budget (seconds) per tool call for the retrieval of additional symbol information
# such as docstrings or parameter information.
# This overrides the corresponding setting in the global configuration; see the documentation there.
# If null or missing, use the setting from the global configuration.
symbol_info_budget:
+37 -4
View File
@@ -1,6 +1,6 @@
.PHONY: build-all build-api build-admin build-node build-queue build-scheduler build-rpc-core \
gen-api gen-model docker-up docker-down docker-env-up docker-env-down lint clean \
run-api run-admin run-node run-queue run-scheduler run-rpc-core
run-api run-admin run-node run-queue run-scheduler run-rpc-core migrate migrate-up migrate-down migrate-create migrate-version migrate-force migrate-goto
# Environment: dev | test | prod (default: dev)
ENV ?= dev
@@ -47,9 +47,9 @@ run-rpc-core:
# Code generation
gen-api:
cd apps/api && goctl api go -api api.api -dir . -style goZero
cd apps/admin && goctl api go -api admin.api -dir . -style goZero
cd apps/node && goctl api go -api node.api -dir . -style goZero
cd apps/api && goctl api go -api api.api -dir . -style goZero -home ../../goctl_tpl
cd apps/admin && goctl api go -api admin.api -dir . -style goZero -home ../../goctl_tpl
cd apps/node && goctl api go -api node.api -dir . -style goZero -home ../../goctl_tpl
# fix: admin routes.go 中 server 包名与参数名 server(*rest.Server) 冲突
# 1. 若 goctl 生成了 server import 但没加别名,加上别名
sed -i '' 's|"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/server"|serverhandler "github.com/zero-ppanel/zero-ppanel/apps/admin/internal/handler/server"|g' apps/admin/internal/handler/routes.go
@@ -81,3 +81,36 @@ lint:
clean:
rm -rf bin/
# Database migrations (using go-migrate)
migrate:
@echo "Usage: make migrate <command> [args...]"
@echo "Commands: up, down [version], create <name>, version, force <version>, goto <version>"
migrate-up:
@echo "Running database migration UP..."
go run pkg/migrate/main.go up
migrate-down:
@echo "Running database migration DOWN..."
ifeq ($(ARGS),)
go run pkg/migrate/main.go down
else
go run pkg/migrate/main.go down $(ARGS)
endif
migrate-create:
@echo "Creating new migration files..."
go run pkg/migrate/main.go create $(NAME)
migrate-version:
@echo "Getting current database migration version..."
go run pkg/migrate/main.go version
migrate-force:
@echo "Forcing database migration version..."
go run pkg/migrate/main.go force $(VERSION)
migrate-goto:
@echo "Going to specific database migration version..."
go run pkg/migrate/main.go goto $(VERSION)
+14
View File
@@ -30,3 +30,17 @@ MySQL:
Redis:
Host: 127.0.0.1:6379
Type: node
CacheRedis:
Host: 127.0.0.1:6379
Type: node
AppSignature:
AppSecrets:
android-client: "uB4G,XxL2{7b"
web-client: "uB4G,XxL2{7b"
ios-client: "uB4G,XxL2{7b"
mac-client: "uB4G,XxL2{7b"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/admin/health
+15
View File
@@ -34,3 +34,18 @@ Redis:
Host: "${REDIS_HOST}"
Type: node
Pass: "${REDIS_PASS}"
CacheRedis:
Host: "${REDIS_HOST}"
Type: node
Pass: "${REDIS_PASS}"
AppSignature:
AppSecrets:
android-client: "${APP_SECRET}"
web-client: "${APP_SECRET}"
ios-client: "${APP_SECRET}"
mac-client: "${APP_SECRET}"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/admin/health
+14
View File
@@ -32,3 +32,17 @@ MySQL:
Redis:
Host: redis:6379
Type: node
CacheRedis:
Host: redis:6379
Type: node
AppSignature:
AppSecrets:
android-client: "uB4G,XxL2{7b"
web-client: "uB4G,XxL2{7b"
ios-client: "uB4G,XxL2{7b"
mac-client: "uB4G,XxL2{7b"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/admin/health
+7 -1
View File
@@ -3,7 +3,11 @@
package config
import "github.com/zeromicro/go-zero/rest"
import (
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
)
type Config struct {
rest.RestConf
@@ -11,4 +15,6 @@ type Config struct {
AccessSecret string
AccessExpire int64
}
CacheRedis redis.RedisConf
AppSignature signature.SignatureConf
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/announcement"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateAnnouncementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateAnnouncementReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := announcement.NewCreateAnnouncementLogic(r.Context(), svcCtx)
resp, err := l.CreateAnnouncement(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/announcement"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func DeleteAnnouncementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteAnnouncementReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := announcement.NewDeleteAnnouncementLogic(r.Context(), svcCtx)
err := l.DeleteAnnouncement(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/announcement"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateAnnouncementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateAnnouncementReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := announcement.NewUpdateAnnouncementLogic(r.Context(), svcCtx)
err := l.UpdateAnnouncement(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewHealthLogic(r.Context(), svcCtx)
resp, err := l.Health()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/console"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetConsoleStatsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := console.NewGetConsoleStatsLogic(r.Context(), svcCtx)
resp, err := l.GetConsoleStats()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/order"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetOrderListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminOrderListReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := order.NewGetOrderListLogic(r.Context(), svcCtx)
resp, err := l.GetOrderList(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/order"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateOrderStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateOrderStatusReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := order.NewUpdateOrderStatusLogic(r.Context(), svcCtx)
err := l.UpdateOrderStatus(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateServerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateServerReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := server.NewCreateServerLogic(r.Context(), svcCtx)
resp, err := l.CreateServer(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func DeleteServerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteServerReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := server.NewDeleteServerLogic(r.Context(), svcCtx)
err := l.DeleteServer(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetServerListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ServerListReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := server.NewGetServerListLogic(r.Context(), svcCtx)
resp, err := l.GetServerList(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/server"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateServerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateServerReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := server.NewUpdateServerLogic(r.Context(), svcCtx)
err := l.UpdateServer(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateSubscribeReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := subscribe.NewCreateSubscribeLogic(r.Context(), svcCtx)
resp, err := l.CreateSubscribe(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func DeleteSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteSubscribeReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := subscribe.NewDeleteSubscribeLogic(r.Context(), svcCtx)
err := l.DeleteSubscribe(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetSubscribeListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminSubscribeListReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := subscribe.NewGetSubscribeListLogic(r.Context(), svcCtx)
resp, err := l.GetSubscribeList(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/subscribe"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateSubscribeReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := subscribe.NewUpdateSubscribeLogic(r.Context(), svcCtx)
err := l.UpdateSubscribe(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetRegisterConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := system.NewGetRegisterConfigLogic(r.Context(), svcCtx)
resp, err := l.GetRegisterConfig()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetSiteConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := system.NewGetSiteConfigLogic(r.Context(), svcCtx)
resp, err := l.GetSiteConfig()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateRegisterConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateRegisterConfigReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := system.NewUpdateRegisterConfigLogic(r.Context(), svcCtx)
err := l.UpdateRegisterConfig(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/system"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateSiteConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateSiteConfigReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := system.NewUpdateSiteConfigLogic(r.Context(), svcCtx)
err := l.UpdateSiteConfig(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateTicketFollowHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminCreateTicketFollowReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewCreateTicketFollowLogic(r.Context(), svcCtx)
err := l.CreateTicketFollow(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetTicketDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminTicketDetailReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewGetTicketDetailLogic(r.Context(), svcCtx)
resp, err := l.GetTicketDetail(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetTicketListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminTicketListReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewGetTicketListLogic(r.Context(), svcCtx)
resp, err := l.GetTicketList(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateTicketStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUpdateTicketStatusReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewUpdateTicketStatusLogic(r.Context(), svcCtx)
err := l.UpdateTicketStatus(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/user"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func DeleteUserHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminDeleteUserReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := user.NewDeleteUserLogic(r.Context(), svcCtx)
err := l.DeleteUser(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/user"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetUserDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUserDetailReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := user.NewGetUserDetailLogic(r.Context(), svcCtx)
resp, err := l.GetUserDetail(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/logic/user"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.AdminUserListReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := user.NewGetUserListLogic(r.Context(), svcCtx)
resp, err := l.GetUserList(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -0,0 +1,88 @@
package middleware
import (
"bytes"
"io"
"net/http"
"strings"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/config"
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/rest/httpx"
)
type SignatureMiddleware struct {
conf config.Config
validator *signature.Validator
}
func NewSignatureMiddleware(c config.Config, store signature.NonceStore) *SignatureMiddleware {
return &SignatureMiddleware{
conf: c,
validator: signature.NewValidator(c.AppSignature, store),
}
}
func (m *SignatureMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
appId := r.Header.Get("X-App-Id")
if appId == "" {
next(w, r)
return
}
for _, prefix := range m.conf.AppSignature.SkipPrefixes {
if strings.HasPrefix(r.URL.Path, prefix) {
next(w, r)
return
}
}
timestamp := r.Header.Get("X-Timestamp")
nonce := r.Header.Get("X-Nonce")
sig := r.Header.Get("X-Signature")
if timestamp == "" || nonce == "" || sig == "" {
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(xerr.SignatureMissing))
return
}
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
sts := signature.BuildStringToSign(r.Method, r.URL.Path, r.URL.RawQuery, bodyBytes, appId, timestamp, nonce)
if err := m.validator.Validate(r.Context(), appId, timestamp, nonce, sig, sts); err != nil {
code := mapSignatureErr(err)
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(code))
return
}
next(w, r)
}
}
func mapSignatureErr(err error) int {
switch err {
case signature.ErrSignatureMissing:
return xerr.SignatureMissing
case signature.ErrSignatureExpired:
return xerr.SignatureExpired
case signature.ErrSignatureReplay:
return xerr.SignatureReplay
default:
return xerr.SignatureInvalid
}
}
func buildErrResp(code int) map[string]interface{} {
return map[string]interface{}{
"code": code,
"msg": xerr.MapErrMsg(code),
"data": nil,
}
}
@@ -5,14 +5,22 @@ package svc
import (
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/config"
"github.com/zero-ppanel/zero-ppanel/apps/admin/internal/middleware"
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zeromicro/go-zero/core/stores/redis"
)
type ServiceContext struct {
Config config.Config
SignatureMiddleware *middleware.SignatureMiddleware
}
func NewServiceContext(c config.Config) *ServiceContext {
rds := redis.MustNewRedis(c.CacheRedis)
nonceStore := signature.NewRedisNonceStore(rds)
return &ServiceContext{
Config: c,
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
}
}
+2 -1
View File
@@ -21,12 +21,13 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
server.Use(ctx.SignatureMiddleware.Handle)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
+51 -17
View File
@@ -6,26 +6,48 @@ info (
type (
UserLoginReq {
Email string `json:"email"`
Password string `json:"password"`
Identifier string `json:"identifier"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
IP string `header:"X-Original-Forwarded-For,optional"`
UserAgent string `header:"User-Agent,optional"`
LoginType string `header:"Login-Type,optional"`
CfToken string `json:"cf_token,optional"`
}
LoginResp {
Token string `json:"token"`
}
DeviceLoginReq {
Identifier string `json:"identifier" validate:"required"`
UserAgent string `json:"user_agent" validate:"required"`
ShortCode string `json:"short_code,optional"`
CfToken string `json:"cf_token,optional"`
IP string `header:"X-Original-Forwarded-For,optional"`
}
UserRegisterReq {
Email string `json:"email"`
Password string `json:"password"`
Code string `json:"code"`
ReferCode string `json:"refer_code,optional"`
}
AuthResp {
Token string `json:"token"`
Expire int64 `json:"expire"`
Identifier string `json:"identifier"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Invite string `json:"invite,optional"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For,optional"`
UserAgent string `header:"User-Agent,optional"`
LoginType string `header:"Login-Type,optional"`
CfToken string `json:"cf_token,optional"`
}
ResetPasswordReq {
Email string `json:"email"`
Password string `json:"password"`
Code string `json:"code"`
Identifier string `json:"identifier"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For,optional"`
UserAgent string `header:"User-Agent,optional"`
LoginType string `header:"Login-Type,optional"`
CfToken string `json:"cf_token,optional"`
}
)
@@ -35,11 +57,23 @@ type (
)
service ppanel-api {
@handler UserLoginHandler
post /login (UserLoginReq) returns (AuthResp)
post /login (UserLoginReq) returns (LoginResp)
@handler UserRegisterHandler
post /register (UserRegisterReq) returns (AuthResp)
post /register (UserRegisterReq) returns (LoginResp)
@handler ResetPasswordHandler
post /reset_password (ResetPasswordReq)
post /reset (ResetPasswordReq) returns (LoginResp)
}
@server (
prefix: /api/v1/auth
group: auth
middleware: DecryptMiddleware
)
service ppanel-api {
@doc "设备登录"
@handler DeviceLoginHandler
post /login/device (DeviceLoginReq) returns (LoginResp)
}
+21
View File
@@ -31,6 +31,27 @@ Redis:
Host: 127.0.0.1:6379
Type: node
CacheRedis:
Host: 127.0.0.1:6379
Type: node
AppSignature:
AppSecrets:
android-client: "uB4G,XxL2{7b}"
web-client: "uB4G,XxL2{7b}"
ios-client: "uB4G,XxL2{7b}"
mac-client: "uB4G,XxL2{7b}"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/health
Security:
Enable: true
SecuritySecret: "uB4G,XxL2{7b"
Device:
Enable: true
Asynq:
Addr: 127.0.0.1:6379
+22
View File
@@ -35,6 +35,28 @@ Redis:
Type: node
Pass: "${REDIS_PASS}"
CacheRedis:
Host: "${REDIS_HOST}"
Type: node
Pass: "${REDIS_PASS}"
AppSignature:
AppSecrets:
android-client: "${APP_SECRET}"
web-client: "${APP_SECRET}"
ios-client: "${APP_SECRET}"
mac-client: "${APP_SECRET}"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/health
Security:
Enable: true
SecuritySecret: "${SECURITY_SECRET}"
Asynq:
Addr: "${REDIS_HOST}"
Pass: "${REDIS_PASS}"
CoreRpc:
Target: "${CORE_RPC_TARGET}"
+18
View File
@@ -33,5 +33,23 @@ Redis:
Host: redis:6379
Type: node
CacheRedis:
Host: redis:6379
Type: node
AppSignature:
AppSecrets:
android-client: "uB4G,XxL2{7b"
web-client: "uB4G,XxL2{7b"
ios-client: "uB4G,XxL2{7b"
mac-client: "uB4G,XxL2{7b"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/health
Security:
Enable: true
SecuritySecret: "uB4G,XxL2{7b"
Asynq:
Addr: redis:6379
+11
View File
@@ -4,6 +4,8 @@
package config
import (
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
)
@@ -15,4 +17,13 @@ type Config struct {
AccessExpire int64
}
CoreRpc zrpc.RpcClientConf
CacheRedis redis.RedisConf
AppSignature signature.SignatureConf
Security struct {
Enable bool
SecuritySecret string
}
Device struct {
Enable bool
}
}
@@ -0,0 +1,28 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package auth
import (
"net/http"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/auth"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
// 设备登录
func DeviceLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeviceLoginReq
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := auth.NewDeviceLoginLogic(r.Context(), svcCtx)
resp, err := l.DeviceLogin(&req)
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/auth"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func ResetPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ResetPasswordReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := auth.NewResetPasswordLogic(r.Context(), svcCtx)
err := l.ResetPassword(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
resp, err := l.ResetPassword(&req)
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/auth"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UserLoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UserLoginReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := auth.NewUserLoginLogic(r.Context(), svcCtx)
resp, err := l.UserLogin(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/auth"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UserRegisterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UserRegisterReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := auth.NewUserRegisterLogic(r.Context(), svcCtx)
resp, err := l.UserRegister(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetAnnouncementListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewGetAnnouncementListLogic(r.Context(), svcCtx)
resp, err := l.GetAnnouncementList()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetAvailablePaymentMethodsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewGetAvailablePaymentMethodsLogic(r.Context(), svcCtx)
resp, err := l.GetAvailablePaymentMethods()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetDocumentDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DocumentDetailReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := common.NewGetDocumentDetailLogic(r.Context(), svcCtx)
resp, err := l.GetDocumentDetail(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetDocumentListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewGetDocumentListLogic(r.Context(), svcCtx)
resp, err := l.GetDocumentList()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetGlobalConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewGetGlobalConfigLogic(r.Context(), svcCtx)
resp, err := l.GetGlobalConfig()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetSubscribeGroupListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewGetSubscribeGroupListLogic(r.Context(), svcCtx)
resp, err := l.GetSubscribeGroupList()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetSubscribeListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewGetSubscribeListLogic(r.Context(), svcCtx)
resp, err := l.GetSubscribeList()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewHealthLogic(r.Context(), svcCtx)
resp, err := l.Health()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func SendEmailCodeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.SendEmailCodeReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := common.NewSendEmailCodeLogic(r.Context(), svcCtx)
err := l.SendEmailCode(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/order"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CloseOrderHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CloseOrderReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := order.NewCloseOrderLogic(r.Context(), svcCtx)
err := l.CloseOrder(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/order"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateOrderHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateOrderReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := order.NewCreateOrderLogic(r.Context(), svcCtx)
resp, err := l.CreateOrder(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/order"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetOrderDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.OrderDetailReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := order.NewGetOrderDetailLogic(r.Context(), svcCtx)
resp, err := l.GetOrderDetail(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
+16 -1
View File
@@ -31,13 +31,28 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
},
{
Method: http.MethodPost,
Path: "/reset_password",
Path: "/reset",
Handler: auth.ResetPasswordHandler(serverCtx),
},
},
rest.WithPrefix("/api/v1/auth"),
)
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.DecryptMiddleware},
[]rest.Route{
{
// 设备登录
Method: http.MethodPost,
Path: "/login/device",
Handler: auth.DeviceLoginHandler(serverCtx),
},
}...,
),
rest.WithPrefix("/api/v1/auth"),
)
server.AddRoutes(
[]rest.Route{
{
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateTicketFollowHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateTicketFollowReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewCreateTicketFollowLogic(r.Context(), svcCtx)
err := l.CreateTicketFollow(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func CreateTicketHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CreateTicketReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewCreateTicketLogic(r.Context(), svcCtx)
resp, err := l.CreateTicket(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/ticket"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetTicketDetailHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.TicketDetailReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := ticket.NewGetTicketDetailLogic(r.Context(), svcCtx)
resp, err := l.GetTicketDetail(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/user"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetUserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := user.NewGetUserInfoLogic(r.Context(), svcCtx)
resp, err := l.GetUserInfo()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/user"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetUserSubscribeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := user.NewGetUserSubscribeLogic(r.Context(), svcCtx)
resp, err := l.GetUserSubscribe()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/logic/user"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func UpdateUserPasswordHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdatePasswordReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := user.NewUpdateUserPasswordLogic(r.Context(), svcCtx)
err := l.UpdateUserPassword(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -0,0 +1,92 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.9.2
package auth
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
"github.com/zero-ppanel/zero-ppanel/pkg/jwtx"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/core/logx"
)
type DeviceLoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 设备登录
func NewDeviceLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceLoginLogic {
return &DeviceLoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginReq) (resp *types.LoginResp, err error) {
// todo: 新建设备登录是否禁用逻辑
if !l.svcCtx.Config.Device.Enable {
return nil, xerr.NewErrCode(xerr.DeviceLoginDisabled)
}
// 1. 调用 Core RPC 设备登录
rpcResp, err := l.svcCtx.CoreRpc.DeviceLogin(l.ctx, &core.DeviceLoginReq{
Identifier: req.Identifier,
UserAgent: req.UserAgent,
Ip: req.IP,
ShortCode: req.ShortCode,
})
if err != nil {
return nil, err
}
// 2. 检查用户是否禁用
if rpcResp.IsDisabled {
return nil, xerr.NewErrCode(xerr.UserDisabled)
}
// 3. 生成 session ID
sessionId := uuid.New().String()
// 4. 生成 JWT Token
token, err := jwtx.GenerateToken(
l.svcCtx.Config.JwtAuth.AccessSecret,
rpcResp.UserId,
"device",
rpcResp.IsAdmin,
l.svcCtx.Config.JwtAuth.AccessExpire,
)
if err != nil {
l.Errorf("GenerateToken error: %v", err)
return nil, xerr.NewErrCode(xerr.ServerError)
}
// 5. 写入 Redis session
expireDuration := time.Duration(l.svcCtx.Config.JwtAuth.AccessExpire) * time.Second
// session:{sessionId} → userId
sessionKey := fmt.Sprintf("session:%s", sessionId)
if err := l.svcCtx.Redis.SetexCtx(l.ctx, sessionKey, fmt.Sprintf("%d", rpcResp.UserId), int(expireDuration.Seconds())); err != nil {
l.Errorf("Redis set session error: %v", err)
}
// device:{identifier} → sessionId
deviceKey := fmt.Sprintf("device:%s", req.Identifier)
if err := l.svcCtx.Redis.SetexCtx(l.ctx, deviceKey, sessionId, int(expireDuration.Seconds())); err != nil {
l.Errorf("Redis set device session error: %v", err)
}
return &types.LoginResp{
Token: token,
}, nil
}
@@ -26,8 +26,8 @@ func NewResetPasswordLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Res
}
}
func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordReq) error {
func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordReq) (resp *types.LoginResp, err error) {
// todo: add your logic here and delete this line
return nil
return
}
@@ -26,7 +26,7 @@ func NewUserLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLog
}
}
func (l *UserLoginLogic) UserLogin(req *types.UserLoginReq) (resp *types.AuthResp, err error) {
func (l *UserLoginLogic) UserLogin(req *types.UserLoginReq) (resp *types.LoginResp, err error) {
// todo: add your logic here and delete this line
return
@@ -26,7 +26,7 @@ func NewUserRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *User
}
}
func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterReq) (resp *types.AuthResp, err error) {
func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterReq) (resp *types.LoginResp, err error) {
// todo: add your logic here and delete this line
return
+2 -13
View File
@@ -8,7 +8,6 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/types"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -28,17 +27,7 @@ func NewHealthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *HealthLogi
}
func (l *HealthLogic) Health() (resp *types.HealthResp, err error) {
// 调用 RPC 进行 Ping 测试,这能触发全链路追踪 (API -> RPC)
rpcResp, err := l.svcCtx.CoreRpc.Ping(l.ctx, &core.Empty{})
// todo: add your logic here and delete this line
status := "ok"
if err != nil {
status = "rpc_error: " + err.Error()
} else if rpcResp.Msg != "" {
status = rpcResp.Msg
}
return &types.HealthResp{
Status: status,
}, nil
return
}
@@ -0,0 +1,163 @@
package middleware
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"strings"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
"github.com/zero-ppanel/zero-ppanel/pkg/cryptox"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/rest/httpx"
)
type DecryptMiddleware struct {
conf config.Config
}
func NewDecryptMiddleware(c config.Config) *DecryptMiddleware {
return &DecryptMiddleware{conf: c}
}
func (m *DecryptMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !m.conf.Security.Enable {
next(w, r)
return
}
if r.Header.Get("Login-Type") != "device" {
next(w, r)
return
}
secret := m.conf.Security.SecuritySecret
rw := newEncryptResponseWriter(w, secret)
// 解密 GET query
query := r.URL.Query()
dataStr := query.Get("data")
timeStr := query.Get("time")
if dataStr != "" && timeStr != "" {
if plain, err := cryptox.Decrypt(dataStr, secret, timeStr); err == nil {
params := map[string]interface{}{}
if json.Unmarshal(plain, &params) == nil {
for k, v := range params {
query.Set(k, fmt.Sprintf("%v", v))
}
query.Del("data")
query.Del("time")
rawQuery := query.Encode()
if strings.Contains(r.RequestURI, "?") {
r.RequestURI = r.RequestURI[:strings.Index(r.RequestURI, "?")] + "?" + rawQuery
}
r.URL.RawQuery = rawQuery
}
}
}
// 解密 POST body
if r.Body != nil {
body, err := io.ReadAll(r.Body)
if err != nil || len(body) == 0 {
// body 为空或读取失败,直接放行
r.Body = io.NopCloser(bytes.NewBuffer(body))
next(rw, r)
rw.flush()
return
}
var envelope struct {
Data string `json:"data"`
Time string `json:"time"`
}
if err := json.Unmarshal(body, &envelope); err != nil || envelope.Data == "" {
httpx.Error(w, xerr.NewErrCode(xerr.DecryptFailed))
return
}
plain, err := cryptox.Decrypt(envelope.Data, secret, envelope.Time)
if err != nil {
httpx.Error(w, xerr.NewErrCode(xerr.DecryptFailed))
return
}
r.Body = io.NopCloser(bytes.NewBuffer(plain))
// 防止 httpx.Parse 内部 r.ParseForm() 消费已替换的 body。
// Go 标准库 ParseForm 首行检查 r.PostForm==nil 才读 body,提前置空即可跳过。
r.PostForm = url.Values{}
r.ContentLength = int64(len(plain))
}
next(rw, r)
rw.flush()
}
}
// encryptResponseWriter 拦截响应,加密 data 字段
type encryptResponseWriter struct {
http.ResponseWriter
body *bytes.Buffer
secret string
status int
}
func newEncryptResponseWriter(w http.ResponseWriter, secret string) *encryptResponseWriter {
return &encryptResponseWriter{
ResponseWriter: w,
body: new(bytes.Buffer),
secret: secret,
status: http.StatusOK,
}
}
func (rw *encryptResponseWriter) WriteHeader(code int) {
rw.status = code
}
func (rw *encryptResponseWriter) Write(data []byte) (int, error) {
return rw.body.Write(data)
}
func (rw *encryptResponseWriter) WriteString(s string) (int, error) {
return rw.body.WriteString(s)
}
func (rw *encryptResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return rw.ResponseWriter.(http.Hijacker).Hijack()
}
func (rw *encryptResponseWriter) flush() {
buf := rw.body.Bytes()
out := buf
// 尝试加密 data 字段
params := map[string]interface{}{}
if err := json.Unmarshal(buf, &params); err == nil {
if data := params["data"]; data != nil {
var jsonData []byte
if str, ok := data.(string); ok {
jsonData = []byte(str)
} else {
jsonData, _ = json.Marshal(data)
}
if dataB64, nonce, err := cryptox.Encrypt(jsonData, rw.secret); err == nil {
params["data"] = map[string]interface{}{
"data": dataB64,
"time": nonce,
}
if enc, err := json.Marshal(params); err == nil {
out = enc
}
}
}
}
rw.ResponseWriter.WriteHeader(rw.status)
rw.ResponseWriter.Write(out)
}
@@ -0,0 +1,83 @@
package middleware
import (
"bytes"
"io"
"net/http"
"strings"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/rest/httpx"
)
type SignatureMiddleware struct {
conf config.Config
validator *signature.Validator
}
func NewSignatureMiddleware(c config.Config, store signature.NonceStore) *SignatureMiddleware {
return &SignatureMiddleware{
conf: c,
validator: signature.NewValidator(c.AppSignature, store),
}
}
func (m *SignatureMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
appId := r.Header.Get("X-App-Id")
// X-App-Id 为空,提示非法访问
if appId == "" {
httpx.Error(w, xerr.NewErrCode(xerr.InvalidAccess))
return
}
// SkipPrefixes 白名单
for _, prefix := range m.conf.AppSignature.SkipPrefixes {
if strings.HasPrefix(r.URL.Path, prefix) {
next(w, r)
return
}
}
timestamp := r.Header.Get("X-Timestamp")
nonce := r.Header.Get("X-Nonce")
sig := r.Header.Get("X-Signature")
if timestamp == "" || nonce == "" || sig == "" {
httpx.Error(w, xerr.NewErrCode(xerr.SignatureMissing))
return
}
// 读取 body(签名对原始 body bytes 计算)
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
sts := signature.BuildStringToSign(r.Method, r.URL.Path, r.URL.RawQuery, bodyBytes, appId, timestamp, nonce)
if err := m.validator.Validate(r.Context(), appId, timestamp, nonce, sig, sts); err != nil {
code := mapSignatureErr(err)
httpx.Error(w, xerr.NewErrCode(code))
return
}
next(w, r)
}
}
func mapSignatureErr(err error) int {
switch err {
case signature.ErrSignatureMissing:
return xerr.SignatureMissing
case signature.ErrSignatureExpired:
return xerr.SignatureExpired
case signature.ErrSignatureReplay:
return xerr.SignatureReplay
default:
return xerr.SignatureInvalid
}
}
+14 -1
View File
@@ -5,18 +5,31 @@ package svc
import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreclient"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/middleware"
coreClient "github.com/zero-ppanel/zero-ppanel/apps/rpc/core/coreClient"
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
)
type ServiceContext struct {
Config config.Config
CoreRpc coreClient.Core
Redis *redis.Redis
SignatureMiddleware *middleware.SignatureMiddleware
DecryptMiddleware rest.Middleware
}
func NewServiceContext(c config.Config) *ServiceContext {
rds := redis.MustNewRedis(c.CacheRedis)
nonceStore := signature.NewRedisNonceStore(rds)
return &ServiceContext{
Config: c,
CoreRpc: coreClient.NewCore(zrpc.MustNewClient(c.CoreRpc)),
Redis: rds,
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
DecryptMiddleware: middleware.NewDecryptMiddleware(c).Handle,
}
}
+36 -14
View File
@@ -10,11 +10,6 @@ type AnnouncementResp struct {
CreatedAt string `json:"created_at"`
}
type AuthResp struct {
Token string `json:"token"`
Expire int64 `json:"expire"`
}
type CloseOrderReq struct {
OrderNo string `path:"order_no"`
}
@@ -36,6 +31,14 @@ type CreateTicketReq struct {
Content string `json:"content"`
}
type DeviceLoginReq struct {
Identifier string `json:"identifier" validate:"required"`
UserAgent string `json:"user_agent" validate:"required"`
ShortCode string `json:"short_code,optional"`
CfToken string `json:"cf_token,optional"`
IP string `header:"X-Original-Forwarded-For,optional"`
}
type DocumentDetailReq struct {
Id int64 `path:"id"`
}
@@ -63,6 +66,10 @@ type HealthResp struct {
Status string `json:"status"`
}
type LoginResp struct {
Token string `json:"token"`
}
type OrderDetailReq struct {
OrderNo string `path:"order_no"`
}
@@ -84,9 +91,14 @@ type PaymentMethodResp struct {
}
type ResetPasswordReq struct {
Email string `json:"email"`
Password string `json:"password"`
Code string `json:"code"`
Identifier string `json:"identifier"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For,optional"`
UserAgent string `header:"User-Agent,optional"`
LoginType string `header:"Login-Type,optional"`
CfToken string `json:"cf_token,optional"`
}
type SendEmailCodeReq struct {
@@ -132,15 +144,25 @@ type UserInfoResp struct {
}
type UserLoginReq struct {
Email string `json:"email"`
Password string `json:"password"`
Identifier string `json:"identifier"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
IP string `header:"X-Original-Forwarded-For,optional"`
UserAgent string `header:"User-Agent,optional"`
LoginType string `header:"Login-Type,optional"`
CfToken string `json:"cf_token,optional"`
}
type UserRegisterReq struct {
Email string `json:"email"`
Password string `json:"password"`
Code string `json:"code"`
ReferCode string `json:"refer_code,optional"`
Identifier string `json:"identifier"`
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
Invite string `json:"invite,optional"`
Code string `json:"code,optional"`
IP string `header:"X-Original-Forwarded-For,optional"`
UserAgent string `header:"User-Agent,optional"`
LoginType string `header:"Login-Type,optional"`
CfToken string `json:"cf_token,optional"`
}
type UserSubscribeResp struct {
+7 -1
View File
@@ -10,9 +10,11 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/config"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/handler"
"github.com/zero-ppanel/zero-ppanel/apps/api/internal/svc"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/rest/httpx"
)
var configFile = flag.String("f", "etc/api-dev.yaml", "the config file")
@@ -21,14 +23,18 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
server.Use(ctx.SignatureMiddleware.Handle)
handler.RegisterHandlers(server, ctx)
// Registe global http error handler
httpx.SetErrorHandler(result.ErrHandler)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
+14
View File
@@ -28,3 +28,17 @@ MySQL:
Redis:
Host: 127.0.0.1:6379
Type: node
CacheRedis:
Host: 127.0.0.1:6379
Type: node
AppSignature:
AppSecrets:
android-client: "uB4G,XxL2{7b"
web-client: "uB4G,XxL2{7b"
ios-client: "uB4G,XxL2{7b"
mac-client: "uB4G,XxL2{7b"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/node/health
+15
View File
@@ -32,3 +32,18 @@ Redis:
Host: "${REDIS_HOST}"
Type: node
Pass: "${REDIS_PASS}"
CacheRedis:
Host: "${REDIS_HOST}"
Type: node
Pass: "${REDIS_PASS}"
AppSignature:
AppSecrets:
android-client: "${APP_SECRET}"
web-client: "${APP_SECRET}"
ios-client: "${APP_SECRET}"
mac-client: "${APP_SECRET}"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/node/health
+14
View File
@@ -30,3 +30,17 @@ MySQL:
Redis:
Host: redis:6379
Type: node
CacheRedis:
Host: redis:6379
Type: node
AppSignature:
AppSecrets:
android-client: "uB4G,XxL2{7b"
web-client: "uB4G,XxL2{7b"
ios-client: "uB4G,XxL2{7b"
mac-client: "uB4G,XxL2{7b"
ValidWindowSeconds: 300
SkipPrefixes:
- /api/v1/node/health
+8 -1
View File
@@ -3,8 +3,15 @@
package config
import "github.com/zeromicro/go-zero/rest"
import (
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
)
type Config struct {
rest.RestConf
NodeSecret string
CacheRedis redis.RedisConf
AppSignature signature.SignatureConf
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/common"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func HealthHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := common.NewHealthLogic(r.Context(), svcCtx)
resp, err := l.Health()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetServerConfigHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := node.NewGetServerConfigLogic(r.Context(), svcCtx)
resp, err := l.GetServerConfig()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -8,17 +8,13 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func GetServerUserListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := node.NewGetServerUserListLogic(r.Context(), svcCtx)
resp, err := l.GetServerUserList()
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
result.HttpResult(r, w, resp, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func PushOnlineUsersHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.PushOnlineUsersReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := node.NewPushOnlineUsersLogic(r.Context(), svcCtx)
err := l.PushOnlineUsers(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func PushStatusHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.PushStatusReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := node.NewPushStatusLogic(r.Context(), svcCtx)
err := l.PushStatus(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -9,23 +9,19 @@ import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/logic/node"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/svc"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zero-ppanel/zero-ppanel/pkg/result"
)
func PushUserTrafficHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.PushUserTrafficReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
if err := result.Parse(r, &req); err != nil {
result.HttpResult(r, w, nil, err)
return
}
l := node.NewPushUserTrafficLogic(r.Context(), svcCtx)
err := l.PushUserTraffic(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.Ok(w)
}
result.HttpResult(r, w, nil, err)
}
}
@@ -0,0 +1,88 @@
package middleware
import (
"bytes"
"io"
"net/http"
"strings"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/config"
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/rest/httpx"
)
type SignatureMiddleware struct {
conf config.Config
validator *signature.Validator
}
func NewSignatureMiddleware(c config.Config, store signature.NonceStore) *SignatureMiddleware {
return &SignatureMiddleware{
conf: c,
validator: signature.NewValidator(c.AppSignature, store),
}
}
func (m *SignatureMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
appId := r.Header.Get("X-App-Id")
if appId == "" {
next(w, r)
return
}
for _, prefix := range m.conf.AppSignature.SkipPrefixes {
if strings.HasPrefix(r.URL.Path, prefix) {
next(w, r)
return
}
}
timestamp := r.Header.Get("X-Timestamp")
nonce := r.Header.Get("X-Nonce")
sig := r.Header.Get("X-Signature")
if timestamp == "" || nonce == "" || sig == "" {
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(xerr.SignatureMissing))
return
}
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
sts := signature.BuildStringToSign(r.Method, r.URL.Path, r.URL.RawQuery, bodyBytes, appId, timestamp, nonce)
if err := m.validator.Validate(r.Context(), appId, timestamp, nonce, sig, sts); err != nil {
code := mapSignatureErr(err)
httpx.WriteJson(w, http.StatusUnauthorized, buildErrResp(code))
return
}
next(w, r)
}
}
func mapSignatureErr(err error) int {
switch err {
case signature.ErrSignatureMissing:
return xerr.SignatureMissing
case signature.ErrSignatureExpired:
return xerr.SignatureExpired
case signature.ErrSignatureReplay:
return xerr.SignatureReplay
default:
return xerr.SignatureInvalid
}
}
func buildErrResp(code int) map[string]interface{} {
return map[string]interface{}{
"code": code,
"msg": xerr.MapErrMsg(code),
"data": nil,
}
}
+7
View File
@@ -6,17 +6,24 @@ package svc
import (
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/config"
"github.com/zero-ppanel/zero-ppanel/apps/node/internal/middleware"
"github.com/zero-ppanel/zero-ppanel/pkg/signature"
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/rest"
)
type ServiceContext struct {
Config config.Config
NodeAuthMiddleware rest.Middleware
SignatureMiddleware *middleware.SignatureMiddleware
}
func NewServiceContext(c config.Config) *ServiceContext {
rds := redis.MustNewRedis(c.CacheRedis)
nonceStore := signature.NewRedisNonceStore(rds)
return &ServiceContext{
Config: c,
NodeAuthMiddleware: middleware.NewNodeAuthMiddleware().Handle,
SignatureMiddleware: middleware.NewSignatureMiddleware(c, nonceStore),
}
}
+2 -1
View File
@@ -21,12 +21,13 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
ctx := svc.NewServiceContext(c)
server.Use(ctx.SignatureMiddleware.Handle)
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
+1 -1
View File
@@ -19,7 +19,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
logx.MustSetup(c.Log)
if c.Telemetry.Name != "" {
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
+24
View File
@@ -18,6 +18,7 @@ message BasicResponse {
// ----------------------------------------------------------------------------
message GetUserInfoReq {
int64 id = 1;
string email = 2; // 用于根据邮箱查询用户
}
message GetUserInfoResp {
@@ -25,6 +26,9 @@ message GetUserInfoResp {
string email = 2;
string role = 3;
string uuid = 4;
string password = 5; // 用于验证密码
bool is_disabled = 6; // 用户是否被禁用
bool is_deleted = 7; // 用户是否已被删除
}
// ----------------------------------------------------------------------------
@@ -41,6 +45,23 @@ message GetNodeInfoResp {
string status = 4;
}
// ----------------------------------------------------------------------------
// Device 服务定义
// ----------------------------------------------------------------------------
message DeviceLoginReq {
string identifier = 1;
string user_agent = 2;
string ip = 3;
string short_code = 4;
}
message DeviceLoginResp {
int64 user_id = 1;
bool is_admin = 2;
bool is_disabled = 3;
bool is_new_user = 4;
}
// ----------------------------------------------------------------------------
// 核心 RPC 服务接口
// ----------------------------------------------------------------------------
@@ -53,4 +74,7 @@ service Core {
// 节点相关
rpc GetNodeInfo(GetNodeInfoReq) returns(GetNodeInfoResp);
// 设备登录
rpc DeviceLogin(DeviceLoginReq) returns(DeviceLoginResp);
}
+211 -14
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.4
// protoc-gen-go v1.36.10
// protoc v4.25.2
// source: core.proto
package core
@@ -118,6 +118,7 @@ func (x *BasicResponse) GetMsg() string {
type GetUserInfoReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` // 用于根据邮箱查询用户
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -159,12 +160,22 @@ func (x *GetUserInfoReq) GetId() int64 {
return 0
}
func (x *GetUserInfoReq) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
type GetUserInfoResp struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
Role string `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"`
Uuid string `protobuf:"bytes,4,opt,name=uuid,proto3" json:"uuid,omitempty"`
Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"` // 用于验证密码
IsDisabled bool `protobuf:"varint,6,opt,name=is_disabled,json=isDisabled,proto3" json:"is_disabled,omitempty"` // 用户是否被禁用
IsDeleted bool `protobuf:"varint,7,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"` // 用户是否已被删除
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -227,6 +238,27 @@ func (x *GetUserInfoResp) GetUuid() string {
return ""
}
func (x *GetUserInfoResp) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
func (x *GetUserInfoResp) GetIsDisabled() bool {
if x != nil {
return x.IsDisabled
}
return false
}
func (x *GetUserInfoResp) GetIsDeleted() bool {
if x != nil {
return x.IsDeleted
}
return false
}
// ----------------------------------------------------------------------------
// Node 服务定义
// ----------------------------------------------------------------------------
@@ -342,6 +374,145 @@ func (x *GetNodeInfoResp) GetStatus() string {
return ""
}
// ----------------------------------------------------------------------------
// Device 服务定义
// ----------------------------------------------------------------------------
type DeviceLoginReq struct {
state protoimpl.MessageState `protogen:"open.v1"`
Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"`
UserAgent string `protobuf:"bytes,2,opt,name=user_agent,json=userAgent,proto3" json:"user_agent,omitempty"`
Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"`
ShortCode string `protobuf:"bytes,4,opt,name=short_code,json=shortCode,proto3" json:"short_code,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeviceLoginReq) Reset() {
*x = DeviceLoginReq{}
mi := &file_core_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeviceLoginReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeviceLoginReq) ProtoMessage() {}
func (x *DeviceLoginReq) ProtoReflect() protoreflect.Message {
mi := &file_core_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeviceLoginReq.ProtoReflect.Descriptor instead.
func (*DeviceLoginReq) Descriptor() ([]byte, []int) {
return file_core_proto_rawDescGZIP(), []int{6}
}
func (x *DeviceLoginReq) GetIdentifier() string {
if x != nil {
return x.Identifier
}
return ""
}
func (x *DeviceLoginReq) GetUserAgent() string {
if x != nil {
return x.UserAgent
}
return ""
}
func (x *DeviceLoginReq) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
func (x *DeviceLoginReq) GetShortCode() string {
if x != nil {
return x.ShortCode
}
return ""
}
type DeviceLoginResp struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
IsAdmin bool `protobuf:"varint,2,opt,name=is_admin,json=isAdmin,proto3" json:"is_admin,omitempty"`
IsDisabled bool `protobuf:"varint,3,opt,name=is_disabled,json=isDisabled,proto3" json:"is_disabled,omitempty"`
IsNewUser bool `protobuf:"varint,4,opt,name=is_new_user,json=isNewUser,proto3" json:"is_new_user,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeviceLoginResp) Reset() {
*x = DeviceLoginResp{}
mi := &file_core_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeviceLoginResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeviceLoginResp) ProtoMessage() {}
func (x *DeviceLoginResp) ProtoReflect() protoreflect.Message {
mi := &file_core_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeviceLoginResp.ProtoReflect.Descriptor instead.
func (*DeviceLoginResp) Descriptor() ([]byte, []int) {
return file_core_proto_rawDescGZIP(), []int{7}
}
func (x *DeviceLoginResp) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *DeviceLoginResp) GetIsAdmin() bool {
if x != nil {
return x.IsAdmin
}
return false
}
func (x *DeviceLoginResp) GetIsDisabled() bool {
if x != nil {
return x.IsDisabled
}
return false
}
func (x *DeviceLoginResp) GetIsNewUser() bool {
if x != nil {
return x.IsNewUser
}
return false
}
var File_core_proto protoreflect.FileDescriptor
const file_core_proto_rawDesc = "" +
@@ -351,25 +522,47 @@ const file_core_proto_rawDesc = "" +
"\x05Empty\"5\n" +
"\rBasicResponse\x12\x12\n" +
"\x04code\x18\x01 \x01(\x05R\x04code\x12\x10\n" +
"\x03msg\x18\x02 \x01(\tR\x03msg\" \n" +
"\x03msg\x18\x02 \x01(\tR\x03msg\"6\n" +
"\x0eGetUserInfoReq\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\"_\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x14\n" +
"\x05email\x18\x02 \x01(\tR\x05email\"\xbb\x01\n" +
"\x0fGetUserInfoResp\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x14\n" +
"\x05email\x18\x02 \x01(\tR\x05email\x12\x12\n" +
"\x04role\x18\x03 \x01(\tR\x04role\x12\x12\n" +
"\x04uuid\x18\x04 \x01(\tR\x04uuid\" \n" +
"\x04uuid\x18\x04 \x01(\tR\x04uuid\x12\x1a\n" +
"\bpassword\x18\x05 \x01(\tR\bpassword\x12\x1f\n" +
"\vis_disabled\x18\x06 \x01(\bR\n" +
"isDisabled\x12\x1d\n" +
"\n" +
"is_deleted\x18\a \x01(\bR\tisDeleted\" \n" +
"\x0eGetNodeInfoReq\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\"e\n" +
"\x0fGetNodeInfoResp\x12\x0e\n" +
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
"\x04name\x18\x02 \x01(\tR\x04name\x12\x16\n" +
"\x06server\x18\x03 \x01(\tR\x06server\x12\x16\n" +
"\x06status\x18\x04 \x01(\tR\x06status2\xa8\x01\n" +
"\x06status\x18\x04 \x01(\tR\x06status\"~\n" +
"\x0eDeviceLoginReq\x12\x1e\n" +
"\n" +
"identifier\x18\x01 \x01(\tR\n" +
"identifier\x12\x1d\n" +
"\n" +
"user_agent\x18\x02 \x01(\tR\tuserAgent\x12\x0e\n" +
"\x02ip\x18\x03 \x01(\tR\x02ip\x12\x1d\n" +
"\n" +
"short_code\x18\x04 \x01(\tR\tshortCode\"\x86\x01\n" +
"\x0fDeviceLoginResp\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x19\n" +
"\bis_admin\x18\x02 \x01(\bR\aisAdmin\x12\x1f\n" +
"\vis_disabled\x18\x03 \x01(\bR\n" +
"isDisabled\x12\x1e\n" +
"\vis_new_user\x18\x04 \x01(\bR\tisNewUser2\xe4\x01\n" +
"\x04Core\x12(\n" +
"\x04Ping\x12\v.core.Empty\x1a\x13.core.BasicResponse\x12:\n" +
"\vGetUserInfo\x12\x14.core.GetUserInfoReq\x1a\x15.core.GetUserInfoResp\x12:\n" +
"\vGetNodeInfo\x12\x14.core.GetNodeInfoReq\x1a\x15.core.GetNodeInfoRespB\bZ\x06./coreb\x06proto3"
"\vGetNodeInfo\x12\x14.core.GetNodeInfoReq\x1a\x15.core.GetNodeInfoResp\x12:\n" +
"\vDeviceLogin\x12\x14.core.DeviceLoginReq\x1a\x15.core.DeviceLoginRespB\bZ\x06./coreb\x06proto3"
var (
file_core_proto_rawDescOnce sync.Once
@@ -383,7 +576,7 @@ func file_core_proto_rawDescGZIP() []byte {
return file_core_proto_rawDescData
}
var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_core_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_core_proto_goTypes = []any{
(*Empty)(nil), // 0: core.Empty
(*BasicResponse)(nil), // 1: core.BasicResponse
@@ -391,16 +584,20 @@ var file_core_proto_goTypes = []any{
(*GetUserInfoResp)(nil), // 3: core.GetUserInfoResp
(*GetNodeInfoReq)(nil), // 4: core.GetNodeInfoReq
(*GetNodeInfoResp)(nil), // 5: core.GetNodeInfoResp
(*DeviceLoginReq)(nil), // 6: core.DeviceLoginReq
(*DeviceLoginResp)(nil), // 7: core.DeviceLoginResp
}
var file_core_proto_depIdxs = []int32{
0, // 0: core.Core.Ping:input_type -> core.Empty
2, // 1: core.Core.GetUserInfo:input_type -> core.GetUserInfoReq
4, // 2: core.Core.GetNodeInfo:input_type -> core.GetNodeInfoReq
1, // 3: core.Core.Ping:output_type -> core.BasicResponse
3, // 4: core.Core.GetUserInfo:output_type -> core.GetUserInfoResp
5, // 5: core.Core.GetNodeInfo:output_type -> core.GetNodeInfoResp
3, // [3:6] is the sub-list for method output_type
0, // [0:3] is the sub-list for method input_type
6, // 3: core.Core.DeviceLogin:input_type -> core.DeviceLoginReq
1, // 4: core.Core.Ping:output_type -> core.BasicResponse
3, // 5: core.Core.GetUserInfo:output_type -> core.GetUserInfoResp
5, // 6: core.Core.GetNodeInfo:output_type -> core.GetNodeInfoResp
7, // 7: core.Core.DeviceLogin:output_type -> core.DeviceLoginResp
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
@@ -417,7 +614,7 @@ func file_core_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_core_proto_rawDesc), len(file_core_proto_rawDesc)),
NumEnums: 0,
NumMessages: 6,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
+46 -6
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v6.33.4
// - protoc-gen-go-grpc v1.5.1
// - protoc v4.25.2
// source: core.proto
package core
@@ -22,6 +22,7 @@ const (
Core_Ping_FullMethodName = "/core.Core/Ping"
Core_GetUserInfo_FullMethodName = "/core.Core/GetUserInfo"
Core_GetNodeInfo_FullMethodName = "/core.Core/GetNodeInfo"
Core_DeviceLogin_FullMethodName = "/core.Core/DeviceLogin"
)
// CoreClient is the client API for Core service.
@@ -38,6 +39,8 @@ type CoreClient interface {
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
// 节点相关
GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts ...grpc.CallOption) (*GetNodeInfoResp, error)
// 设备登录
DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error)
}
type coreClient struct {
@@ -78,6 +81,16 @@ func (c *coreClient) GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts .
return out, nil
}
func (c *coreClient) DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeviceLoginResp)
err := c.cc.Invoke(ctx, Core_DeviceLogin_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// CoreServer is the server API for Core service.
// All implementations must embed UnimplementedCoreServer
// for forward compatibility.
@@ -92,6 +105,8 @@ type CoreServer interface {
GetUserInfo(context.Context, *GetUserInfoReq) (*GetUserInfoResp, error)
// 节点相关
GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error)
// 设备登录
DeviceLogin(context.Context, *DeviceLoginReq) (*DeviceLoginResp, error)
mustEmbedUnimplementedCoreServer()
}
@@ -103,13 +118,16 @@ type CoreServer interface {
type UnimplementedCoreServer struct{}
func (UnimplementedCoreServer) Ping(context.Context, *Empty) (*BasicResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Ping not implemented")
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
}
func (UnimplementedCoreServer) GetUserInfo(context.Context, *GetUserInfoReq) (*GetUserInfoResp, error) {
return nil, status.Error(codes.Unimplemented, "method GetUserInfo not implemented")
return nil, status.Errorf(codes.Unimplemented, "method GetUserInfo not implemented")
}
func (UnimplementedCoreServer) GetNodeInfo(context.Context, *GetNodeInfoReq) (*GetNodeInfoResp, error) {
return nil, status.Error(codes.Unimplemented, "method GetNodeInfo not implemented")
return nil, status.Errorf(codes.Unimplemented, "method GetNodeInfo not implemented")
}
func (UnimplementedCoreServer) DeviceLogin(context.Context, *DeviceLoginReq) (*DeviceLoginResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeviceLogin not implemented")
}
func (UnimplementedCoreServer) mustEmbedUnimplementedCoreServer() {}
func (UnimplementedCoreServer) testEmbeddedByValue() {}
@@ -122,7 +140,7 @@ type UnsafeCoreServer interface {
}
func RegisterCoreServer(s grpc.ServiceRegistrar, srv CoreServer) {
// If the following call panics, it indicates UnimplementedCoreServer was
// If the following call pancis, it indicates UnimplementedCoreServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@@ -186,6 +204,24 @@ func _Core_GetNodeInfo_Handler(srv interface{}, ctx context.Context, dec func(in
return interceptor(ctx, in, info, handler)
}
func _Core_DeviceLogin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeviceLoginReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CoreServer).DeviceLogin(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Core_DeviceLogin_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CoreServer).DeviceLogin(ctx, req.(*DeviceLoginReq))
}
return interceptor(ctx, in, info, handler)
}
// Core_ServiceDesc is the grpc.ServiceDesc for Core service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -205,6 +241,10 @@ var Core_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetNodeInfo",
Handler: _Core_GetNodeInfo_Handler,
},
{
MethodName: "DeviceLogin",
Handler: _Core_DeviceLogin_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "core.proto",
+10
View File
@@ -15,6 +15,8 @@ import (
type (
BasicResponse = core.BasicResponse
DeviceLoginReq = core.DeviceLoginReq
DeviceLoginResp = core.DeviceLoginResp
Empty = core.Empty
GetNodeInfoReq = core.GetNodeInfoReq
GetNodeInfoResp = core.GetNodeInfoResp
@@ -28,6 +30,8 @@ type (
GetUserInfo(ctx context.Context, in *GetUserInfoReq, opts ...grpc.CallOption) (*GetUserInfoResp, error)
// 节点相关
GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts ...grpc.CallOption) (*GetNodeInfoResp, error)
// 设备登录
DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error)
}
defaultCore struct {
@@ -58,3 +62,9 @@ func (m *defaultCore) GetNodeInfo(ctx context.Context, in *GetNodeInfoReq, opts
client := core.NewCoreClient(m.cli.Conn())
return client.GetNodeInfo(ctx, in, opts...)
}
// 设备登录
func (m *defaultCore) DeviceLogin(ctx context.Context, in *DeviceLoginReq, opts ...grpc.CallOption) (*DeviceLoginResp, error) {
client := core.NewCoreClient(m.cli.Conn())
return client.DeviceLogin(ctx, in, opts...)
}
+6
View File
@@ -0,0 +1,6 @@
Name: core.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: core.rpc
@@ -0,0 +1,104 @@
package logic
import (
"context"
"database/sql"
"errors"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/repo"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/svc"
"github.com/zero-ppanel/zero-ppanel/pkg/xerr"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type DeviceLoginLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDeviceLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceLoginLogic {
return &DeviceLoginLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// DeviceLogin 设备登录:查设备 → 不存在则注册 → 返回用户信息
func (l *DeviceLoginLogic) DeviceLogin(in *core.DeviceLoginReq) (*core.DeviceLoginResp, error) {
// 1. 查询设备
device, err := l.svcCtx.DeviceRepo.FindDeviceByIdentifier(l.ctx, in.Identifier)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
l.Errorf("FindDeviceByIdentifier error: %v", err)
return nil, status.Error(codes.Code(xerr.DatabaseQueryError), "查询设备失败")
}
var userID int64
var isNewUser bool
var isAdmin bool
if errors.Is(err, sql.ErrNoRows) || device == nil {
// 2. 设备不存在,创建用户+设备
userID, err = l.svcCtx.DeviceRepo.CreateUserWithDevice(l.ctx, repo.CreateUserWithDeviceParams{
Identifier: in.Identifier,
UserAgent: in.UserAgent,
IP: in.Ip,
ShortCode: in.ShortCode,
})
if err != nil {
l.Errorf("CreateUserWithDevice error: %v", err)
return nil, status.Error(codes.Code(xerr.DatabaseInsertError), "创建用户设备失败")
}
isNewUser = true
// 记录注册日志
_ = l.svcCtx.DeviceRepo.InsertLoginLog(l.ctx, repo.LoginLogParams{
UserID: userID,
Method: "device",
IP: in.Ip,
UserAgent: in.UserAgent,
Success: true,
})
} else {
// 3. 设备存在,检查是否启用
if !device.Enabled {
return nil, status.Error(codes.Code(xerr.DeviceNotEnabled), "设备已禁用")
}
userID = device.UserID
// 查用户信息
user, err := l.svcCtx.DeviceRepo.FindUserByID(l.ctx, userID)
if err != nil {
l.Errorf("FindUserByID error: %v", err)
return nil, status.Error(codes.Code(xerr.DatabaseQueryError), "查询用户失败")
}
if !user.Enable {
return nil, status.Error(codes.Code(xerr.UserDisabled), "用户已禁用")
}
isAdmin = user.IsAdmin
}
// 4. 记录登录日志
_ = l.svcCtx.DeviceRepo.InsertLoginLog(l.ctx, repo.LoginLogParams{
UserID: userID,
Method: "device",
IP: in.Ip,
UserAgent: in.UserAgent,
Success: true,
})
return &core.DeviceLoginResp{
UserId: userID,
IsAdmin: isAdmin,
IsDisabled: false,
IsNewUser: isNewUser,
}, nil
}
@@ -2,6 +2,7 @@ package logic
import (
"context"
"errors"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/core"
"github.com/zero-ppanel/zero-ppanel/apps/rpc/core/internal/svc"
@@ -25,7 +26,48 @@ func NewGetUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUs
// 用户相关
func (l *GetUserInfoLogic) GetUserInfo(in *core.GetUserInfoReq) (*core.GetUserInfoResp, error) {
// todo: add your logic here and delete this line
return &core.GetUserInfoResp{}, nil
// 暂不支持空参数查询
if in.Email == "" && in.Id == 0 {
return nil, errors.New("id or email is required")
}
// TODO: 根据实际的 DB model 查询用户信息
// 假设您后续引入了 userModel 并放入了 svcCtx,下面是标准写法:
//
// var userInfo *model.User
// var err error
// if in.Email != "" {
// userInfo, err = l.svcCtx.UserModel.FindOneByEmail(l.ctx, in.Email)
// } else {
// userInfo, err = l.svcCtx.UserModel.FindOne(l.ctx, in.Id)
// }
//
// if err != nil {
// if errors.Is(err, sqlc.ErrNotFound) {
// return &core.GetUserInfoResp{}, nil // 用户不存在,由业务层通过 Resp 的零值判断
// }
// return nil, err
// }
// =============== Mock 数据用于联调测试 ===============
// 为了使您的 API 服务现在的登录重构直接能进行 Postman 联调,
// 此处返回一个模拟存在的用户(您可以直接用这个邮箱及秘密进行登录尝试)。
// Mock Email: admin@admin.com
// Mock Password: admin (对应的 bcrypted hash)
if in.Email == "admin@admin.com" || in.Id == 1 {
return &core.GetUserInfoResp{
Id: 1,
Email: "admin@admin.com",
Role: "admin", // 模拟管理员
Password: "$2a$10$X8H.V2hG1E8c3rT5fH8h3.3nK290X8t9gY1N4/n.9s3.m4G0W.3yW", // `admin` 加密后的结果
Uuid: "mock-uuid-123",
IsDisabled: false,
IsDeleted: false,
}, nil
}
// 模拟未找到用户
return &core.GetUserInfoResp{
Id: 0, // Id=0 表示没找到
}, nil
}

Some files were not shown because too many files have changed in this diff Show More