feat: 补齐第一批已知缺口(关闭握手 / 定时备份 / Origin 白名单 / DTO 收敛)

- httpapi:新增 POST /api/shutdown(admin 会话或 X-Shutdown-Token 常量时间比对)
  与 GET /api/rules/switches(recorder 可读,只回布尔开关);settings/standings 的
  重复 DTO 收敛到 internal/dto;standings.showIndividualMedals 贯通到设置读写与前端
- export:抽出 export.Service,新增自动备份定时任务(backup.intervalMinutes/keep),
  自动、手动、发布前三类备份共用同一把互斥锁与滚动清理
- realtime:/ws 去掉 InsecureSkipVerify,改用 OriginPatterns(同源与无 Origin 放行,
  跨站需命中 server.allowedOrigins)
- console:停止服务改走 POST /api/shutdown 优雅关闭(5 秒宽限后回退强杀),
  服务管理器新增「已连接的大屏实例」卡片
- deploy:build.bat 改用 for /f 读内部署名——set /p 在 chcp 65001 下返回空,
  导致最近这次构建的 exe 里根本没注入署名
- docs:备份策略、关闭超时、Origin 配置的说明与实现对齐
This commit is contained in:
AskaEth committed 2026-09-09 21:41:08 +08:00
1 parent 4d56c73ae6
commit 2c9ff65fc4
42 files changed
+1271 -275

No files matched your search

+20
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"git.yeij.top/AskaEth/Scoreboard/server/internal/auth"
"git.yeij.top/AskaEth/Scoreboard/server/internal/dto"
"git.yeij.top/AskaEth/Scoreboard/server/internal/rules"
)
@@ -13,6 +14,25 @@ import (
func (s *server) rulesRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /api/rules/export", s.read(auth.RoleAdmin, s.exportRules))
mux.HandleFunc("POST /api/rules/import", s.write(auth.RoleAdmin, s.importRules))
// 录入员要判断破纪录加分/人工调整是否启用,但无权读 /api/settings,故单开只读开关。
mux.HandleFunc("GET /api/rules/switches", s.read(auth.RoleRecorder, s.ruleSwitches))
}
// ruleSwitches 只返回布尔开关,不下发规则内容、模板明细或文件路径。
func (s *server) ruleSwitches(w http.ResponseWriter, r *http.Request) (any, error) {
snap, err := rules.Load(s.ev)
if err != nil {
return nil, internal(err)
}
pack := snap.Pack
return dto.RuleSwitches{
RecordBonusEnabled: pack.RecordBonus.Enabled,
AdjustmentsEnabled: pack.ManualAdjustment.Enabled,
AdjustmentReasonRequired: pack.ManualAdjustment.ReasonRequired,
MedalCountsTeamEvents: pack.Standings.MedalCountsTeamEventsEnabled(),
ShowIndividualBoard: pack.Standings.ShowIndividualBoardEnabled(),
ShowIndividualMedals: pack.Standings.ShowIndividualMedalsEnabled(),
}, nil
}
func (s *server) exportRules(w http.ResponseWriter, r *http.Request) (any, error) {