Files
lmvpn_server/internal/db/db.go
T
kevin c1d560fd4a feat: 会话管理 - 支持按设备粒度踢下线
- 新增 sessions 表,每次登录创建会话记录(记录 IP/UA)
- AuthMiddleware 改为基于 session 校验,可精确踢掉单个设备
- 自己改密码后踢掉其他设备,管理员改密码后踢掉该用户全部设备
- 新增 GET /api/me/sessions 查看活跃会话列表
- 新增 DELETE /api/me/sessions/:sessionId 踢掉指定会话
- 新增 DELETE /api/admin/users/:id/sessions 管理员强制下线某用户
- 兼容无 session_id 的旧 token,回退到 TokenInvalidBefore 校验
2026-07-03 10:52:39 +08:00

77 lines
1.4 KiB
Go

package db
import (
"fmt"
"log"
"lmvpn/internal/config"
"lmvpn/internal/model"
"github.com/glebarez/sqlite"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func Init(cfg *config.DatabaseConfig) error {
var d gorm.Dialector
switch cfg.Type {
case "sqlite":
d = sqlite.Open(cfg.Path)
case "mysql":
if cfg.DSN == "" {
return fmt.Errorf("mysql DSN 不能为空")
}
d = mysql.Open(cfg.DSN)
default:
return fmt.Errorf("不支持的数据库类型: %s", cfg.Type)
}
var err error
DB, err = gorm.Open(d, &gorm.Config{})
if err != nil {
return fmt.Errorf("数据库连接失败: %w", err)
}
if err := DB.AutoMigrate(&model.User{}, &model.Session{}); err != nil {
return fmt.Errorf("数据库迁移失败: %w", err)
}
if err := seedDefaultAdmin(); err != nil {
return fmt.Errorf("创建默认管理员失败: %w", err)
}
log.Printf("数据库初始化完成: %s", cfg.Type)
return nil
}
func seedDefaultAdmin() error {
var count int64
DB.Model(&model.User{}).Count(&count)
if count > 0 {
return nil
}
hash, err := bcrypt.GenerateFromPassword([]byte("admin123"), bcrypt.DefaultCost)
if err != nil {
return err
}
admin := &model.User{
Username: "admin",
Password: string(hash),
Role: "admin",
Status: 1,
}
if err := DB.Create(admin).Error; err != nil {
return err
}
log.Println("已创建默认管理员: admin / admin123")
return nil
}