feat: 认证简化为固定进入密钥 + 页面增强(API 文档/主题切换/版权署名)

- 移除管理员初始化/登录/JWT 流程,改为固定进入密钥(19735500),
  首次启动自动初始化到数据库 settings 表(可改库更新)
- analyze 接口支持可选密钥鉴权:X-Access-Key 请求头或 access_key 字段,
  带密钥强制校验(错误 401),不带密钥保持免鉴权
- 解锁后顶栏"复制 API 文档"按钮:以当前访问 URL 为基础地址动态生成
  Markdown 文档(免鉴权/带密钥两种 curl 示例,供智能体直接调用)
- 白昼/黑夜主题切换:CSS 变量驱动,localStorage 持久化,默认跟随系统
- 修复 hidden 属性被 display:flex 覆盖导致解锁层无法隐藏的问题
- 页面去除"免费/Agnes"文案,页脚署名青梧映星软件开发工作室
- build.bat/build.sh 简化为 Linux 交叉编译(CGO_ENABLED=0)
- 依赖精简:移除 golang-jwt/bcrypt,直接依赖仅 gin/sqlite/gorm/yaml

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-13 01:21:18 +08:00
parent a3bbf33358
commit f9cdeb5d64
20 changed files with 341 additions and 465 deletions

View File

@@ -0,0 +1,26 @@
package service
import (
"gorm.io/gorm"
"vision-tool/internal/model"
)
// AccessService 访问密钥校验。密钥为固定进入密钥,
// 首次启动由 database.Init 初始化到数据库(可后续直接改库)。
type AccessService struct {
db *gorm.DB
}
func NewAccessService(db *gorm.DB) *AccessService {
return &AccessService{db: db}
}
// VerifyKey 校验进入密钥是否与数据库中的值一致。
func (s *AccessService) VerifyKey(key string) bool {
var setting model.Setting
if err := s.db.Where("key = ?", model.KeyAccessKey).First(&setting).Error; err != nil {
return false
}
return setting.Value == key
}

View File

@@ -1,89 +0,0 @@
package service
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"vision-tool/internal/middleware"
"vision-tool/internal/model"
"vision-tool/internal/repository"
)
var (
ErrAdminExists = errors.New("管理员已初始化,禁止重复初始化")
ErrInvalidCredentials = errors.New("用户名或密码错误")
)
// AuthService 认证业务:管理员初始化 / 登录 / 用户信息。
type AuthService struct {
repo *repository.UserRepository
secret string
expire time.Duration
}
func NewAuthService(repo *repository.UserRepository, secret string, expire time.Duration) *AuthService {
return &AuthService{repo: repo, secret: secret, expire: expire}
}
// CheckAdmin 判断是否已存在管理员(方式 A:check + init)。
func (s *AuthService) CheckAdmin() (bool, error) {
count, err := s.repo.Count()
return count > 0, err
}
// InitAdmin 初始化管理员,已存在时拒绝。
func (s *AuthService) InitAdmin(username, password string) error {
initialized, err := s.CheckAdmin()
if err != nil {
return err
}
if initialized {
return ErrAdminExists
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
user := &model.User{
Username: username,
PasswordHash: string(hash),
Role: "admin",
}
return s.repo.Create(user)
}
// Login 校验账号密码,签发 JWT token。
func (s *AuthService) Login(username, password string) (string, error) {
user, err := s.repo.FindByUsername(username)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return "", ErrInvalidCredentials
}
return "", err
}
if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) != nil {
return "", ErrInvalidCredentials
}
claims := middleware.Claims{
UserID: user.ID,
Username: user.Username,
Role: user.Role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(s.expire)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(s.secret))
}
// Profile 按 ID 返回用户信息。
func (s *AuthService) Profile(userID uint) (*model.User, error) {
return s.repo.FindByID(userID)
}