chore: sync local updates
This commit is contained in:
@@ -1,176 +1,93 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config 应用配置
|
||||
type Config struct {
|
||||
Server ServerConfig `json:"server"`
|
||||
Monitor MonitorConfig `json:"monitor"`
|
||||
DataPath string `json:"data_path"`
|
||||
}
|
||||
|
||||
// ServerConfig 服务器配置
|
||||
type ServerConfig struct {
|
||||
Port string `json:"port"`
|
||||
Host string `json:"host"`
|
||||
}
|
||||
|
||||
// MonitorConfig 监控配置
|
||||
type MonitorConfig struct {
|
||||
Interval time.Duration `json:"interval"` // 检测间隔
|
||||
Timeout time.Duration `json:"timeout"` // 请求超时时间
|
||||
RetryCount int `json:"retry_count"` // 重试次数
|
||||
HistoryDays int `json:"history_days"` // 保留历史天数
|
||||
}
|
||||
|
||||
var (
|
||||
cfg *Config
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// GetConfig 获取配置单例
|
||||
func GetConfig() *Config {
|
||||
once.Do(func() {
|
||||
cfg = &Config{
|
||||
Server: ServerConfig{
|
||||
Port: getEnv("SERVER_PORT", "8080"),
|
||||
Host: getEnv("SERVER_HOST", "0.0.0.0"),
|
||||
},
|
||||
Monitor: MonitorConfig{
|
||||
Interval: parseDuration(getEnv("MONITOR_INTERVAL", "1h"), 1*time.Hour),
|
||||
Timeout: parseDuration(getEnv("MONITOR_TIMEOUT", "10s"), 10*time.Second),
|
||||
RetryCount: parseInt(getEnv("MONITOR_RETRY_COUNT", "3"), 3),
|
||||
HistoryDays: parseInt(getEnv("MONITOR_HISTORY_DAYS", "90"), 90),
|
||||
},
|
||||
DataPath: getEnv("DATA_PATH", "./data"),
|
||||
}
|
||||
|
||||
// 尝试从配置文件加载(会覆盖环境变量配置)
|
||||
loadConfigFromFile()
|
||||
})
|
||||
return cfg
|
||||
}
|
||||
|
||||
// getEnv 获取环境变量,如果不存在则返回默认值
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// parseInt 解析整数环境变量
|
||||
func parseInt(value string, defaultValue int) int {
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
var result int
|
||||
if _, err := fmt.Sscanf(value, "%d", &result); err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// parseDuration 解析时间间隔环境变量
|
||||
func parseDuration(value string, defaultValue time.Duration) time.Duration {
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
if duration, err := time.ParseDuration(value); err == nil {
|
||||
return duration
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// loadConfigFromFile 从文件加载配置
|
||||
func loadConfigFromFile() {
|
||||
configFile := "./data/config.json"
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var fileCfg struct {
|
||||
Server ServerConfig `json:"server"`
|
||||
Monitor struct {
|
||||
IntervalMinutes int `json:"interval_minutes"`
|
||||
TimeoutSeconds int `json:"timeout_seconds"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
HistoryDays int `json:"history_days"`
|
||||
} `json:"monitor"`
|
||||
DataPath string `json:"data_path"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &fileCfg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if fileCfg.Server.Port != "" {
|
||||
cfg.Server.Port = fileCfg.Server.Port
|
||||
}
|
||||
if fileCfg.Server.Host != "" {
|
||||
cfg.Server.Host = fileCfg.Server.Host
|
||||
}
|
||||
if fileCfg.Monitor.IntervalMinutes > 0 {
|
||||
cfg.Monitor.Interval = time.Duration(fileCfg.Monitor.IntervalMinutes) * time.Minute
|
||||
}
|
||||
if fileCfg.Monitor.TimeoutSeconds > 0 {
|
||||
cfg.Monitor.Timeout = time.Duration(fileCfg.Monitor.TimeoutSeconds) * time.Second
|
||||
}
|
||||
if fileCfg.Monitor.RetryCount > 0 {
|
||||
cfg.Monitor.RetryCount = fileCfg.Monitor.RetryCount
|
||||
}
|
||||
if fileCfg.Monitor.HistoryDays > 0 {
|
||||
cfg.Monitor.HistoryDays = fileCfg.Monitor.HistoryDays
|
||||
}
|
||||
if fileCfg.DataPath != "" {
|
||||
cfg.DataPath = fileCfg.DataPath
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfig 保存配置到文件
|
||||
func SaveConfig() error {
|
||||
configFile := cfg.DataPath + "/config.json"
|
||||
|
||||
fileCfg := struct {
|
||||
Server ServerConfig `json:"server"`
|
||||
Monitor struct {
|
||||
IntervalMinutes int `json:"interval_minutes"`
|
||||
TimeoutSeconds int `json:"timeout_seconds"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
HistoryDays int `json:"history_days"`
|
||||
} `json:"monitor"`
|
||||
DataPath string `json:"data_path"`
|
||||
}{
|
||||
Server: cfg.Server,
|
||||
Monitor: struct {
|
||||
IntervalMinutes int `json:"interval_minutes"`
|
||||
TimeoutSeconds int `json:"timeout_seconds"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
HistoryDays int `json:"history_days"`
|
||||
}{
|
||||
IntervalMinutes: int(cfg.Monitor.Interval.Minutes()),
|
||||
TimeoutSeconds: int(cfg.Monitor.Timeout.Seconds()),
|
||||
RetryCount: cfg.Monitor.RetryCount,
|
||||
HistoryDays: cfg.Monitor.HistoryDays,
|
||||
},
|
||||
DataPath: cfg.DataPath,
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(fileCfg, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(configFile, data, 0644)
|
||||
}
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config 应用配置(持久化在 MySQL monitor_kv,首次启动前由环境变量提供默认值;MySQL 连接信息首次连接仍依赖环境变量/DB_DSN)
|
||||
type Config struct {
|
||||
Server ServerConfig `json:"server"`
|
||||
Monitor MonitorConfig `json:"monitor"`
|
||||
DataPath string `json:"data_path"`
|
||||
Database DatabaseConfig `json:"database"`
|
||||
}
|
||||
|
||||
// ServerConfig 服务器配置
|
||||
type ServerConfig struct {
|
||||
Port string `json:"port"`
|
||||
Host string `json:"host"`
|
||||
}
|
||||
|
||||
// MonitorConfig 监控配置
|
||||
type MonitorConfig struct {
|
||||
Interval time.Duration `json:"interval"` // 检测间隔
|
||||
Timeout time.Duration `json:"timeout"` // 请求超时时间
|
||||
RetryCount int `json:"retry_count"` // 重试次数
|
||||
HistoryDays int `json:"history_days"` // 保留历史天数
|
||||
}
|
||||
|
||||
var (
|
||||
cfg *Config
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// GetConfig 获取配置单例(连接 MySQL 后由 storage 根据 monitor_kv 覆盖)
|
||||
func GetConfig() *Config {
|
||||
once.Do(func() {
|
||||
cfg = &Config{
|
||||
Server: ServerConfig{
|
||||
Port: getEnv("SERVER_PORT", "8080"),
|
||||
Host: getEnv("SERVER_HOST", "0.0.0.0"),
|
||||
},
|
||||
Monitor: MonitorConfig{
|
||||
Interval: parseDuration(getEnv("MONITOR_INTERVAL", "30m"), 30*time.Minute),
|
||||
Timeout: parseDuration(getEnv("MONITOR_TIMEOUT", "10s"), 10*time.Second),
|
||||
RetryCount: parseInt(getEnv("MONITOR_RETRY_COUNT", "3"), 3),
|
||||
HistoryDays: parseInt(getEnv("MONITOR_HISTORY_DAYS", "90"), 90),
|
||||
},
|
||||
DataPath: getEnv("DATA_PATH", "./data"),
|
||||
Database: DatabaseConfig{
|
||||
Host: getEnv("DB_HOST", "192.168.1.100"),
|
||||
Port: getEnv("DB_PORT", "3306"),
|
||||
User: getEnv("DB_USER", "mengyaping"),
|
||||
Password: getEnv("DB_PASSWORD", "mengyaping"),
|
||||
Database: getEnv("DB_NAME", "mengyaping"),
|
||||
},
|
||||
}
|
||||
})
|
||||
return cfg
|
||||
}
|
||||
|
||||
// getEnv 获取环境变量,如果不存在则返回默认值
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// parseInt 解析整数环境变量
|
||||
func parseInt(value string, defaultValue int) int {
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
var result int
|
||||
if _, err := fmt.Sscanf(value, "%d", &result); err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// parseDuration 解析时间间隔环境变量
|
||||
func parseDuration(value string, defaultValue time.Duration) time.Duration {
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
if duration, err := time.ParseDuration(value); err == nil {
|
||||
return duration
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
25
mengyaping-backend/config/database.go
Normal file
25
mengyaping-backend/config/database.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// DatabaseConfig MySQL 连接(环境变量 DB_* 可覆盖;亦支持 DB_DSN 整串)
|
||||
type DatabaseConfig struct {
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Database string `json:"database"`
|
||||
}
|
||||
|
||||
// DatabaseDSN 返回 GORM/MySQL driver 用连接串
|
||||
func (c *Config) DatabaseDSN() string {
|
||||
if dsn := os.Getenv("DB_DSN"); dsn != "" {
|
||||
return dsn
|
||||
}
|
||||
d := c.Database
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
d.User, d.Password, d.Host, d.Port, d.Database)
|
||||
}
|
||||
38
mengyaping-backend/config/monitor_interval.go
Normal file
38
mengyaping-backend/config/monitor_interval.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package config
|
||||
|
||||
import "slices"
|
||||
|
||||
// AllowedMonitorIntervalMinutes 后台预设的全局检测周期(分钟)
|
||||
var AllowedMonitorIntervalMinutes = []int{10, 20, 30, 60, 180, 360}
|
||||
|
||||
// IsAllowedMonitorInterval 是否为允许的检测间隔(分钟)
|
||||
func IsAllowedMonitorInterval(minutes int) bool {
|
||||
return minutes > 0 && slices.Contains(AllowedMonitorIntervalMinutes, minutes)
|
||||
}
|
||||
|
||||
// SnapMonitorIntervalMinutes 将分钟数裁剪到最近的预设值(用于读取旧配置)
|
||||
func SnapMonitorIntervalMinutes(minutes int) int {
|
||||
if minutes <= 0 {
|
||||
return 30
|
||||
}
|
||||
if IsAllowedMonitorInterval(minutes) {
|
||||
return minutes
|
||||
}
|
||||
best := AllowedMonitorIntervalMinutes[0]
|
||||
bestDist := absInt(minutes - best)
|
||||
for _, v := range AllowedMonitorIntervalMinutes[1:] {
|
||||
d := absInt(minutes - v)
|
||||
if d < bestDist {
|
||||
bestDist = d
|
||||
best = v
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func absInt(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
24
mengyaping-backend/config/monitor_interval_test.go
Normal file
24
mengyaping-backend/config/monitor_interval_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIsAllowedMonitorInterval(t *testing.T) {
|
||||
if !IsAllowedMonitorInterval(10) || !IsAllowedMonitorInterval(360) {
|
||||
t.Fatal("expected 10 and 360 allowed")
|
||||
}
|
||||
if IsAllowedMonitorInterval(5) || IsAllowedMonitorInterval(45) {
|
||||
t.Fatal("expected 5 and 45 disallowed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapMonitorIntervalMinutes(t *testing.T) {
|
||||
if SnapMonitorIntervalMinutes(5) != 10 {
|
||||
t.Fatalf("5 -> 10, got %d", SnapMonitorIntervalMinutes(5))
|
||||
}
|
||||
if SnapMonitorIntervalMinutes(7) != 10 {
|
||||
t.Fatalf("7 -> 10, got %d", SnapMonitorIntervalMinutes(7))
|
||||
}
|
||||
if SnapMonitorIntervalMinutes(25) != 20 {
|
||||
t.Fatalf("25 -> 20, got %d", SnapMonitorIntervalMinutes(25))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user