feat(server): 装配 notices(路由替换空壳+reward/pay 注入)+ 发版联动插 version 公告
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 类型站内公告 (失败不阻断发版)。
This commit is contained in:
@@ -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/
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# deploy-server.sh <tag> — 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 <tag> (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" <<REMOTE
|
||||
set -euo pipefail
|
||||
@@ -50,6 +51,7 @@ cp -p "$BIN/pangolin-server" "$BIN/pangolin-server.bak-$TAG" || true
|
||||
install -m755 /tmp/pangolin-server "$BIN/pangolin-server"
|
||||
install -m755 /tmp/pangolin-agent "$BIN/pangolin-agent"
|
||||
install -m755 /tmp/pangolin-migrate "$BIN/pangolin-migrate"
|
||||
install -m755 /tmp/pangolin-nodectl "$BIN/pangolin-nodectl"
|
||||
systemctl start pangolin-server
|
||||
systemctl is-active pangolin-server
|
||||
REMOTE
|
||||
|
||||
@@ -103,6 +103,15 @@ if [ -f "$MANIFEST_TMPL" ]; then
|
||||
$SCP "$MANIFEST_OUT" "root@${DEPLOY_HOST}:/tmp/pangolin_version.yaml.new"
|
||||
$SSH "root@${DEPLOY_HOST}" "mv /tmp/pangolin_version.yaml.new /etc/pangolin/version.yaml && chown pangolin:pangolin /etc/pangolin/version.yaml && chmod 644 /etc/pangolin/version.yaml"
|
||||
echo "==> 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{}})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user