chore: opt settings code

This commit is contained in:
engigu
2025-12-21 10:23:25 +08:00
parent 432c57e862
commit c06030fe27
28 changed files with 535 additions and 187 deletions
+30 -26
View File
@@ -2,62 +2,66 @@ package services
import (
"baihu/internal/constant"
"encoding/json"
"os"
"gopkg.in/ini.v1"
)
type ServerConfig struct {
Port int `json:"port"`
Host string `json:"host"`
SiteName string `json:"site_name"`
Port int `ini:"port"`
Host string `ini:"host"`
}
type DatabaseConfig struct {
Type string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
DBName string `json:"dbname"`
Path string `json:"path"`
TablePrefix string `json:"table_prefix"`
Type string `ini:"type"`
Host string `ini:"host"`
Port int `ini:"port"`
User string `ini:"user"`
Password string `ini:"password"`
DBName string `ini:"dbname"`
Path string `ini:"path"`
TablePrefix string `ini:"table_prefix"`
}
type SecurityConfig struct {
JWTSecret string `json:"jwt_secret"`
PasswordSalt string `json:"password_salt"`
Secret string `ini:"secret"`
}
type TaskConfig struct {
DefaultTimeout int `json:"default_timeout"`
LogRetentionDays int `json:"log_retention_days"`
DefaultTimeout int `ini:"default_timeout"`
LogRetentionDays int `ini:"log_retention_days"`
}
type AppConfig struct {
Server ServerConfig `json:"server"`
Database DatabaseConfig `json:"database"`
Security SecurityConfig `json:"security"`
Task TaskConfig `json:"task"`
Server ServerConfig `ini:"server"`
Database DatabaseConfig `ini:"database"`
Security SecurityConfig `ini:"security"`
Task TaskConfig `ini:"task"`
}
var Config *AppConfig
func LoadConfig(path string) (*AppConfig, error) {
data, err := os.ReadFile(path)
cfg, err := ini.Load(path)
if err != nil {
return nil, err
}
Config = &AppConfig{}
if err := json.Unmarshal(data, Config); err != nil {
if err := cfg.MapTo(Config); err != nil {
return nil, err
}
// 设置表前缀到 constant 包
if Config.Database.TablePrefix != "" {
constant.TablePrefix = Config.Database.TablePrefix
// 设置默认数据库路径
if Config.Database.Path == "" {
Config.Database.Path = constant.DefaultDBPath
}
// 设置表前缀到 constant 包
constant.TablePrefix = Config.Database.TablePrefix
// 设置 Secret 到 constant 包
constant.Secret = Config.Security.Secret
return Config, nil
}
+2 -35
View File
@@ -1,10 +1,6 @@
package services
import (
"crypto/rand"
"encoding/hex"
"baihu/internal/constant"
"baihu/internal/logger"
)
@@ -27,11 +23,9 @@ func (s *InitService) Initialize() *UserService {
logger.Warnf("初始化设置失败: %v", err)
}
// 初始化 JWT Secret(也用作密码 salt,必须在创建 UserService 之前)
s.initJWTSecret()
// 创建 UserService
userService := NewUserService()
// 创建 UserService(依赖 settingsService 获取 salt
userService := NewUserService(s.settingsService)
// 创建管理员账号
s.initializeAdmin(userService)
@@ -49,30 +43,3 @@ func (s *InitService) initializeAdmin(userService *UserService) {
userService.CreateUser("admin", "123456", "admin@local", "admin")
logger.Info("管理员账号创建成功: admin / 123456")
}
// IsInitialized 检查是否已初始化
func (s *InitService) IsInitialized() bool {
return s.settingsService.Get(constant.SectionSystem, constant.KeyInitialized) == "true"
}
// initJWTSecret 初始化 JWT Secret,如果不存在则生成随机值
func (s *InitService) initJWTSecret() {
existing := s.settingsService.Get(constant.SectionSystem, constant.KeyJWTSecret)
if existing != "" {
return
}
// 生成 32 字节随机密钥
bytes := make([]byte, 32)
if _, err := rand.Read(bytes); err != nil {
logger.Warnf("生成 JWT Secret 失败: %v", err)
return
}
secret := hex.EncodeToString(bytes)
if err := s.settingsService.Set(constant.SectionSystem, constant.KeyJWTSecret, secret); err != nil {
logger.Warnf("保存 JWT Secret 失败: %v", err)
return
}
logger.Info("JWT Secret 已生成")
}
+52
View File
@@ -0,0 +1,52 @@
package services
import (
"baihu/internal/database"
"baihu/internal/models"
)
type LoginLogService struct{}
func NewLoginLogService() *LoginLogService {
return &LoginLogService{}
}
// Create 创建登录日志
func (s *LoginLogService) Create(username, ip, userAgent, status, message string) error {
log := &models.LoginLog{
Username: username,
IP: ip,
UserAgent: userAgent,
Status: status,
Message: message,
}
return database.DB.Create(log).Error
}
// List 获取登录日志列表
func (s *LoginLogService) List(page, pageSize int, username string) ([]models.LoginLog, int64, error) {
var logs []models.LoginLog
var total int64
query := database.DB.Model(&models.LoginLog{})
if username != "" {
query = query.Where("username LIKE ?", "%"+username+"%")
}
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
offset := (page - 1) * pageSize
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&logs).Error; err != nil {
return nil, 0, err
}
return logs, total, nil
}
// CleanOldLogs 清理指定天数前的日志
func (s *LoginLogService) CleanOldLogs(days int) (int64, error) {
result := database.DB.Exec("DELETE FROM "+models.LoginLog{}.TableName()+" WHERE created_at < datetime('now', ?)", "-"+string(rune(days))+" days")
return result.RowsAffected, result.Error
}
+19 -20
View File
@@ -1,6 +1,7 @@
package services
import (
"baihu/internal/cache"
"baihu/internal/constant"
"baihu/internal/database"
"baihu/internal/models"
@@ -17,26 +18,23 @@ func (s *SettingsService) InitSettings() error {
for section, keys := range constant.DefaultSettings {
for key, value := range keys {
var count int64
database.DB.Model(&models.Setting{}).
Where("section = ? AND key = ?", section, key).
Count(&count)
database.DB.Model(&models.Setting{}).Where("section = ? AND key = ?", section, key).Count(&count)
if count == 0 {
setting := &models.Setting{
Section: section,
Key: key,
Value: value,
}
if err := database.DB.Create(setting).Error; err != nil {
if err := database.DB.Create(&models.Setting{Section: section, Key: key, Value: value}).Error; err != nil {
return err
}
}
}
}
cache.LoadSiteCache()
return nil
}
// Get 获取单个设置
func (s *SettingsService) Get(section, key string) string {
if section == constant.SectionSite {
return cache.GetSiteCache(key)
}
var setting models.Setting
if err := database.DB.Where("section = ? AND key = ?", section, key).First(&setting).Error; err != nil {
if def, ok := constant.DefaultSettings[section][key]; ok {
@@ -50,29 +48,27 @@ func (s *SettingsService) Get(section, key string) string {
// Set 设置单个值
func (s *SettingsService) Set(section, key, value string) error {
var setting models.Setting
result := database.DB.Where("section = ? AND key = ?", section, key).First(&setting)
if result.Error != nil {
setting = models.Setting{Section: section, Key: key, Value: value}
return database.DB.Create(&setting).Error
if database.DB.Where("section = ? AND key = ?", section, key).First(&setting).Error != nil {
return database.DB.Create(&models.Setting{Section: section, Key: key, Value: value}).Error
}
return database.DB.Model(&setting).Update("value", value).Error
}
// GetSection 获取整个 section 的设置
func (s *SettingsService) GetSection(section string) map[string]string {
var settings []models.Setting
database.DB.Where("section = ?", section).Find(&settings)
if section == constant.SectionSite {
return cache.GetSiteCacheAll()
}
result := make(map[string]string)
// 先填充默认值
if defaults, ok := constant.DefaultSettings[section]; ok {
for k, v := range defaults {
result[k] = v
}
}
// 覆盖数据库值
for _, s := range settings {
result[s.Key] = s.Value
var settings []models.Setting
database.DB.Where("section = ?", section).Find(&settings)
for _, setting := range settings {
result[setting.Key] = setting.Value
}
return result
}
@@ -84,5 +80,8 @@ func (s *SettingsService) SetSection(section string, values map[string]string) e
return err
}
}
if section == constant.SectionSite {
cache.SetSiteCacheBatch(values)
}
return nil
}
+4 -8
View File
@@ -9,18 +9,14 @@ import (
"baihu/internal/models"
)
type UserService struct {
settingsService *SettingsService
}
type UserService struct{}
func NewUserService(settingsService *SettingsService) *UserService {
return &UserService{settingsService: settingsService}
func NewUserService() *UserService {
return &UserService{}
}
func (us *UserService) hashPassword(password string) string {
// 使用 JWT Secret 作为密码 salt
salt := us.settingsService.Get(constant.SectionSystem, constant.KeyJWTSecret)
hash := sha256.Sum256([]byte(password + salt))
hash := sha256.Sum256([]byte(password + constant.Secret))
return hex.EncodeToString(hash[:])
}