diff --git a/config/config.yaml b/config/config.yaml index eb820b7..3d6c59f 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,7 +1,7 @@ site: name: 文件暂存柜 description: 临时文件中转服务 - logo: "https://www.hxuanyu.com/upload/favicon.png" + logo: https://www.hxuanyu.com/upload/favicon.png security: admin_password_hash: $2a$10$Bm0TEmU4uj.bVHYiIPFBheUkcdg6XHpsanLvmpoAtgU1UnKbo9.vy pickup_code_length: 6 @@ -15,7 +15,7 @@ upload: storage: type: local local: - path: storage_data_test + path: storage_data webdav: url: https://dav.example.com username: user diff --git a/docs/config_specification.md b/docs/config_specification.md index 289a705..cd8dbd2 100644 --- a/docs/config_specification.md +++ b/docs/config_specification.md @@ -16,7 +16,7 @@ | 字段名 | 类型 | 含义 | 示例 | | :--- | :--- | :--- | :--- | -| `admin_password_hash` | string | 管理员密码的 bcrypt 哈希值 | `$2a$10$...` | +| `admin_password_hash` | string | 管理员密码的 bcrypt 哈希值。可以通过更新配置接口修改,修改后立即生效,且不再依赖数据库存储。 | `$2a$10$...` | | `pickup_code_length` | int | 自动生成的取件码长度。变更后系统将自动对存量取件码进行右侧补零或截取以适配新长度。 | `6` | | `pickup_fail_limit` | int | 单个 IP 对单个取件码尝试失败的最大次数,超过后将被临时封禁 | `5` | | `jwt_secret` | string | 用于签发管理端 JWT Token 的密钥,建议设置为复杂随机字符串 | `file-relay-secret` | diff --git a/internal/api/admin/auth.go b/internal/api/admin/auth.go index 0fe4bfa..e4901a5 100644 --- a/internal/api/admin/auth.go +++ b/internal/api/admin/auth.go @@ -2,10 +2,9 @@ package admin import ( "FileRelay/internal/auth" - "FileRelay/internal/bootstrap" + "FileRelay/internal/config" "FileRelay/internal/model" "net/http" - "time" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" @@ -43,27 +42,24 @@ func (h *AuthHandler) Login(c *gin.Context) { return } - var admin model.Admin - if err := bootstrap.DB.First(&admin).Error; err != nil { - c.JSON(http.StatusInternalServerError, model.ErrorResponse(model.CodeInternalError, "Admin not found")) + passwordHash := config.GlobalConfig.Security.AdminPasswordHash + if passwordHash == "" { + c.JSON(http.StatusInternalServerError, model.ErrorResponse(model.CodeInternalError, "Admin password hash not configured")) return } - if err := bcrypt.CompareHashAndPassword([]byte(admin.PasswordHash), []byte(req.Password)); err != nil { + if err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(req.Password)); err != nil { c.JSON(http.StatusUnauthorized, model.ErrorResponse(model.CodeUnauthorized, "Incorrect password")) return } - token, err := auth.GenerateToken(admin.ID) + // 使用固定 ID 1 代表管理员(因为不再有数据库记录) + token, err := auth.GenerateToken(1) if err != nil { c.JSON(http.StatusInternalServerError, model.ErrorResponse(model.CodeInternalError, "Failed to generate token")) return } - // 更新登录时间 - now := time.Now() - bootstrap.DB.Model(&admin).Update("last_login", &now) - c.JSON(http.StatusOK, model.SuccessResponse(LoginResponse{ Token: token, })) diff --git a/internal/bootstrap/init.go b/internal/bootstrap/init.go index 1d76b22..c507faf 100644 --- a/internal/bootstrap/init.go +++ b/internal/bootstrap/init.go @@ -34,7 +34,6 @@ func InitDB() { &model.FileBatch{}, &model.FileItem{}, &model.APIToken{}, - &model.Admin{}, ) if err != nil { log.Fatalf("Failed to migrate database: %v", err) @@ -74,31 +73,28 @@ func ReloadStorage() error { } func initAdmin() { - var count int64 - DB.Model(&model.Admin{}).Count(&count) - if count == 0 { - passwordHash := config.GlobalConfig.Security.AdminPasswordHash - if passwordHash == "" { - // 生成随机密码 - password := generateRandomPassword(12) - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - log.Fatalf("Failed to generate password hash: %v", err) - } - passwordHash = string(hash) - fmt.Printf("**************************************************\n") - fmt.Printf("NO ADMIN PASSWORD CONFIGURED. GENERATED RANDOM PASSWORD:\n") - fmt.Printf("Password: %s\n", password) - fmt.Printf("Please save this password or configure admin_password_hash in config.yaml\n") - fmt.Printf("**************************************************\n") + passwordHash := config.GlobalConfig.Security.AdminPasswordHash + if passwordHash == "" { + // 生成随机密码 + password := generateRandomPassword(12) + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + log.Fatalf("Failed to generate password hash: %v", err) } + passwordHash = string(hash) + fmt.Printf("**************************************************\n") + fmt.Printf("NO ADMIN PASSWORD CONFIGURED. GENERATED RANDOM PASSWORD:\n") + fmt.Printf("Password: %s\n", password) + fmt.Printf("Please save this password or configure admin_password_hash in config.yaml\n") + fmt.Printf("**************************************************\n") - admin := &model.Admin{ - PasswordHash: passwordHash, + // 将生成的哈希保存回配置文件 + config.GlobalConfig.Security.AdminPasswordHash = passwordHash + if err := config.SaveConfig(); err != nil { + fmt.Printf("Warning: Failed to save generated password hash to config: %v\n", err) } - DB.Create(admin) - fmt.Println("Admin account initialized.") } + fmt.Println("Admin authentication initialized via config.") } func generateRandomPassword(length int) string { diff --git a/internal/model/admin.go b/internal/model/admin.go index 2e2b597..5b8f6e5 100644 --- a/internal/model/admin.go +++ b/internal/model/admin.go @@ -4,8 +4,8 @@ import ( "time" ) -type Admin struct { - ID uint `gorm:"primaryKey" json:"id"` - PasswordHash string `json:"-"` - LastLogin *time.Time `json:"last_login"` +// AdminSession 管理员会话信息 (不再存库,仅用于 JWT 或 API 交互) +type AdminSession struct { + ID uint `json:"id"` + LastLogin *time.Time `json:"last_login"` }