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:
1 parent
4d56c73ae6
commit
2c9ff65fc4
42 files changed
+1271
-275
No files matched your search
@@ -14,13 +14,24 @@ import (
|
||||
type Config struct {
|
||||
Server Server `yaml:"server"`
|
||||
DataDir string `yaml:"dataDir"`
|
||||
Backup Backup `yaml:"backup"`
|
||||
Log Log `yaml:"log"`
|
||||
}
|
||||
|
||||
// Backup 是自动备份配置。
|
||||
type Backup struct {
|
||||
// IntervalMinutes 是自动备份间隔(分钟);<=0 表示关闭自动备份。
|
||||
IntervalMinutes int `yaml:"intervalMinutes"`
|
||||
// Keep 是每个库滚动保留的份数;<=0 时用 export.DefaultBackupKeep。
|
||||
Keep int `yaml:"keep"`
|
||||
}
|
||||
|
||||
// Server 是 HTTP 监听配置。
|
||||
type Server struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
// AllowedOrigins 是 WebSocket 跨站 Origin 白名单;为空时只放行同源与不带 Origin 的客户端。
|
||||
AllowedOrigins []string `yaml:"allowedOrigins"`
|
||||
}
|
||||
|
||||
// Log 是日志配置。
|
||||
@@ -33,6 +44,7 @@ func Default() *Config {
|
||||
return &Config{
|
||||
Server: Server{Host: "0.0.0.0", Port: 19540},
|
||||
DataDir: "data",
|
||||
Backup: Backup{IntervalMinutes: 5, Keep: 20},
|
||||
Log: Log{Level: "info"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultAllowedOrigins(t *testing.T) {
|
||||
if got := Default().Server.AllowedOrigins; len(got) != 0 {
|
||||
t.Fatalf("默认白名单应为空: %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAllowedOrigins(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "config.yaml")
|
||||
raw := "server:\n allowedOrigins:\n - http://a.example\n - https://b.example\n"
|
||||
if err := os.WriteFile(path, []byte(raw), 0o644); err != nil {
|
||||
t.Fatalf("写入配置: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := Load(path)
|
||||
if err != nil {
|
||||
t.Fatalf("加载配置: %v", err)
|
||||
}
|
||||
want := []string{"http://a.example", "https://b.example"}
|
||||
if !reflect.DeepEqual(cfg.Server.AllowedOrigins, want) {
|
||||
t.Fatalf("白名单解析错误: got %v want %v", cfg.Server.AllowedOrigins, want)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user