From 2b4350cfc4c3075ca942e277e59cda660030f9a9 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 13 Jul 2026 14:09:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(server):=20=E8=A3=85=E9=85=8D=20notices(?= =?UTF-8?q?=E8=B7=AF=E7=94=B1=E6=9B=BF=E6=8D=A2=E7=A9=BA=E5=A3=B3+reward/p?= =?UTF-8?q?ay=20=E6=B3=A8=E5=85=A5)+=20=E5=8F=91=E7=89=88=E8=81=94?= =?UTF-8?q?=E5=8A=A8=E6=8F=92=20version=20=E5=85=AC=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main.go 挂载 notices.Store/Handler:注入 rewardSvc/payWebhook 到账通知钩子, /v1/notices 路由由 accountAPI 空壳换成真实 List/MarkRead,删除 account.go 的 TODO 空壳方法。补齐 pangolin-nodectl 到编译/部署产物链 (compile-backend.sh + deploy-server.sh),release-client.sh 在 version.yaml 推送成功后追加一步用 nodectl 插入 version 类型站内公告 (失败不阻断发版)。 --- scripts/ci/compile-backend.sh | 10 +++++++--- scripts/ci/deploy-server.sh | 14 ++++++++------ scripts/ci/release-client.sh | 9 +++++++++ server/cmd/server/main.go | 10 +++++++++- server/internal/httpapi/account.go | 9 --------- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/scripts/ci/compile-backend.sh b/scripts/ci/compile-backend.sh index 2a922ea..9ad48d3 100644 --- a/scripts/ci/compile-backend.sh +++ b/scripts/ci/compile-backend.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash # compile-backend.sh — cross-compile the pangolin Go control-plane binaries -# (server / agent / migrate) for the pangolin1 deploy target. CGO disabled: -# modernc.org/sqlite is pure Go, no cgo toolchain needed on the runner. -# Output: server/out/{pangolin-server,pangolin-agent,pangolin-migrate}. +# (server / agent / migrate / nodectl) for the pangolin1 deploy target. CGO +# disabled: modernc.org/sqlite is pure Go, no cgo toolchain needed on the +# runner. +# Output: server/out/{pangolin-server,pangolin-agent,pangolin-migrate,pangolin-nodectl}. # # Run inside a golang:1.25 container by .gitea/workflows/deploy-server.yml; # this script itself just runs `go build` and assumes it is invoked from the @@ -24,5 +25,8 @@ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o out/pangolin-agent ./cmd/agent echo "==> compile-backend: building pangolin-migrate" CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o out/pangolin-migrate ./cmd/migrate +echo "==> compile-backend: building pangolin-nodectl" +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o out/pangolin-nodectl ./cmd/nodectl + echo "==> compile-backend: done — out/ contents:" ls -lh out/ diff --git a/scripts/ci/deploy-server.sh b/scripts/ci/deploy-server.sh index 4d56a1b..91b3414 100644 --- a/scripts/ci/deploy-server.sh +++ b/scripts/ci/deploy-server.sh @@ -1,10 +1,11 @@ #!/usr/bin/env bash # deploy-server.sh — deploy the pangolin control-plane binaries -# (pangolin-server / pangolin-agent / pangolin-migrate) to pangolin1, in the -# exact manual sequence used for F3/F4: stop → wal checkpoint → backup db → -# migrate (rollback db + restart old binary on failure) → swap binaries → -# start → healthcheck. Assumes compile-backend.sh has already produced -# server/out/{pangolin-server,pangolin-agent,pangolin-migrate}. +# (pangolin-server / pangolin-agent / pangolin-migrate / pangolin-nodectl) to +# pangolin1, in the exact manual sequence used for F3/F4: stop → wal +# checkpoint → backup db → migrate (rollback db + restart old binary on +# failure) → swap binaries → start → healthcheck. Assumes compile-backend.sh +# has already produced +# server/out/{pangolin-server,pangolin-agent,pangolin-migrate,pangolin-nodectl}. # # Usage: scripts/ci/deploy-server.sh (e.g. server-v1.2.3) # Requires env: DEPLOY_SSH_KEY (see lib-ssh.sh). @@ -36,7 +37,7 @@ SCP="scp -i ${SSH_KEY_FILE} -P ${DEPLOY_PORT} -o StrictHostKeyChecking=accept-ne echo "==> deploy-server: tag=${TAG} host=${DEPLOY_HOST}" echo "==> deploy-server: uploading binaries to ${DEPLOY_HOST}:/tmp/" -$SCP server/out/pangolin-server server/out/pangolin-agent server/out/pangolin-migrate "root@${DEPLOY_HOST}:/tmp/" +$SCP server/out/pangolin-server server/out/pangolin-agent server/out/pangolin-migrate server/out/pangolin-nodectl "root@${DEPLOY_HOST}:/tmp/" $SSH "root@${DEPLOY_HOST}" "bash -s" < release-client: version.yaml deployed to pangolin1 (/etc/pangolin/version.yaml)" + + # 站内通知:插一条 version 类型公告,提示用户有新版可更新。失败不阻断发版 + # (公告只是锦上添花,manifest 已经推送成功才是自动更新真正生效的部分)。 + echo "==> release-client: inserting version notice via pangolin-nodectl" + $SSH "root@${DEPLOY_HOST}" "DB_DSN=/var/lib/pangolin/pangolin.db DB_DRIVER=sqlite \ + /usr/local/bin/pangolin-nodectl notice add --type version \ + --title-zh \"v${VER} 已发布\" --title-en \"v${VER} released\" \ + --body-zh \"新版本已可更新,打开设置检查更新。\" --body-en \"A new version is available — check for updates in Settings.\"" \ + || echo "==> release-client: version notice 插入失败(不阻断发版)" else echo "==> release-client: DEPLOY_SSH_KEY not set — skipping live manifest push (dist/version.yaml still staged + uploaded as a release asset)" fi diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go index e594141..fd42f53 100644 --- a/server/cmd/server/main.go +++ b/server/cmd/server/main.go @@ -33,6 +33,7 @@ import ( "github.com/wangjia/pangolin/server/internal/httpapi" "github.com/wangjia/pangolin/server/internal/mtls" "github.com/wangjia/pangolin/server/internal/nodes" + "github.com/wangjia/pangolin/server/internal/notices" "github.com/wangjia/pangolin/server/internal/pay" agentv1 "github.com/wangjia/pangolin/server/internal/pb/agentv1" "github.com/wangjia/pangolin/server/internal/provision" @@ -350,6 +351,11 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv authSvc.SetReferralHook(rewardSvc) } + // ── Notices(站内通知;reward/pay 到账钩子注入)──────────────────────────── + noticesStore := notices.NewStore(sqlDB) + noticesHandler := notices.NewHandler(noticesStore) + rewardSvc.SetNoticer(noticesStore) + // ── Pay(pay v2 统一支付网关;PAY_BASE_URL 未配则整组不挂载)────────────── var payHandler *pay.Handler var payWebhook *pay.WebhookHandler @@ -365,6 +371,7 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv payWebhook = pay.NewWebhookHandler(payStore, codesSvc, sqlDB, rdb, paySystem, paySecret, 5*time.Minute, 15*time.Minute) payWebhook.SetRewarder(rewardSvc) + payWebhook.SetNoticer(noticesStore) } else { log.Printf("PAY_BASE_URL 未配置 — /v1/pay 支付端点不挂载") } @@ -482,7 +489,8 @@ func mountV1(r chi.Router, sqlDB *sql.DB, rdb *redis.Client, nodeSvc *nodes.Serv protected.Get("/usage/devices", deviceUsageHandler.ServeHTTP) protected.Post("/ads/unlock", adsHandler.ServeHTTP) protected.Get("/plans", accountAPI.ListPlans) - protected.Get("/notices", accountAPI.ListNotices) + protected.Get("/notices", noticesHandler.List) + protected.Post("/notices/read", noticesHandler.MarkRead) if payHandler != nil { protected.Get("/pay/catalog", payHandler.Catalog) protected.Get("/pay/orders", payHandler.List) diff --git a/server/internal/httpapi/account.go b/server/internal/httpapi/account.go index 4ec90cd..21eb7e4 100644 --- a/server/internal/httpapi/account.go +++ b/server/internal/httpapi/account.go @@ -214,12 +214,3 @@ func (a *AccountAPI) ListPlans(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") _ = json.NewEncoder(w).Encode(map[string]any{"plans": plans}) } - -// ─── GET /v1/notices ───────────────────────────────────────────────────────── - -// ListNotices handles GET /v1/notices. Returns an empty list for the MVP. -// TODO(Task7): 由 notices.Handler 替换后删除(main.go 路由挂载在 Task 7 统一装配)。 -func (a *AccountAPI) ListNotices(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - _ = json.NewEncoder(w).Encode(map[string]any{"notices": []any{}}) -}