mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-21 10:34:26 +08:00
控制器中间件授权改造完成
This commit is contained in:
@@ -103,6 +103,7 @@ func AdminAuthMiddleware(secretKey string, redis *redis.Client) gin.HandlerFunc
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(types.AdminUserID, claims["user_id"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ func (h *ManagerHandler) RegisterRoutes() {
|
||||
// 公开接口,不需要授权
|
||||
group.POST("login", h.Login)
|
||||
group.GET("logout", h.Logout)
|
||||
group.GET("session", h.Session)
|
||||
|
||||
// 需要管理员授权的接口
|
||||
group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.GET("session", h.Session)
|
||||
group.GET("list", h.List)
|
||||
group.POST("save", h.Save)
|
||||
group.POST("enable", h.Enable)
|
||||
@@ -157,16 +157,15 @@ func (h *ManagerHandler) Logout(c *gin.Context) {
|
||||
|
||||
// Session 会话检测
|
||||
func (h *ManagerHandler) Session(c *gin.Context) {
|
||||
id := h.GetLoginUserId(c)
|
||||
key := fmt.Sprintf("admin/%d", id)
|
||||
if _, err := h.redis.Get(context.Background(), key).Result(); err != nil {
|
||||
resp.NotAuth(c)
|
||||
id := h.GetAdminId(c)
|
||||
if id == 0 {
|
||||
resp.NotAuth(c, "当前用户已退出登录")
|
||||
return
|
||||
}
|
||||
var manager model.AdminUser
|
||||
res := h.DB.Where("id", id).First(&manager)
|
||||
if res.Error != nil {
|
||||
resp.NotAuth(c)
|
||||
err := h.DB.Where("id", id).First(&manager).Error
|
||||
if err != nil {
|
||||
resp.NotAuth(c, "当前用户已退出登录")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewApiKeyHandler(app *core.AppServer, db *gorm.DB) *ApiKeyHandler {
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *ApiKeyHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/admin/apiKey/")
|
||||
group := h.App.Engine.Group("/api/admin/apikey/")
|
||||
|
||||
// 需要管理员授权的接口
|
||||
group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis))
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewChatAppHandler(app *core.AppServer, db *gorm.DB) *ChatAppHandler {
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *ChatAppHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/admin/app/")
|
||||
group := h.App.Engine.Group("/api/admin/role/")
|
||||
|
||||
// 需要管理员授权的接口
|
||||
group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis))
|
||||
|
||||
@@ -44,12 +44,12 @@ func NewConfigHandler(app *core.AppServer, db *gorm.DB, levelDB *store.LevelDB,
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *ConfigHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/admin/config/")
|
||||
group.GET("get", h.Get)
|
||||
|
||||
// 需要管理员授权的接口
|
||||
group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.POST("update", h.Update)
|
||||
group.GET("get", h.Get)
|
||||
group.POST("active", h.Active)
|
||||
group.POST("test", h.Test)
|
||||
group.GET("license", h.GetLicense)
|
||||
|
||||
@@ -15,9 +15,10 @@ import (
|
||||
logger2 "geekai/logger"
|
||||
"geekai/store/model"
|
||||
"geekai/utils"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -69,6 +70,14 @@ func (h *BaseHandler) GetLoginUserId(c *gin.Context) uint {
|
||||
return uint(utils.IntValue(utils.InterfaceToString(userId), 0))
|
||||
}
|
||||
|
||||
func (h *BaseHandler) GetAdminId(c *gin.Context) uint {
|
||||
userId, ok := c.Get(types.AdminUserID)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return uint(utils.IntValue(utils.InterfaceToString(userId), 0))
|
||||
}
|
||||
|
||||
func (h *BaseHandler) IsLogin(c *gin.Context) bool {
|
||||
return h.GetLoginUserId(c) > 0
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package handler
|
||||
|
||||
import (
|
||||
"geekai/core"
|
||||
"geekai/core/middleware"
|
||||
"geekai/core/types"
|
||||
"geekai/service"
|
||||
"geekai/utils/resp"
|
||||
@@ -33,14 +32,11 @@ func NewCaptchaHandler(app *core.AppServer, s *service.CaptchaService, sysConfig
|
||||
func (h *CaptchaHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/captcha/")
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.GET("get", h.Get)
|
||||
group.POST("check", h.Check)
|
||||
group.GET("slide/get", h.SlideGet)
|
||||
group.POST("slide/check", h.SlideCheck)
|
||||
}
|
||||
// 无需授权的接口
|
||||
group.GET("get", h.Get)
|
||||
group.POST("check", h.Check)
|
||||
group.GET("slide/get", h.SlideGet)
|
||||
group.POST("slide/check", h.SlideCheck)
|
||||
}
|
||||
|
||||
func (h *CaptchaHandler) Get(c *gin.Context) {
|
||||
|
||||
@@ -30,7 +30,7 @@ func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler {
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *ChatRoleHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/role/")
|
||||
group := h.App.Engine.Group("/api/app/")
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
|
||||
@@ -9,7 +9,6 @@ package handler
|
||||
|
||||
import (
|
||||
"geekai/core"
|
||||
"geekai/core/middleware"
|
||||
"geekai/service"
|
||||
"geekai/store/model"
|
||||
"geekai/utils"
|
||||
@@ -32,12 +31,9 @@ func NewConfigHandler(app *core.AppServer, db *gorm.DB, licenseService *service.
|
||||
func (h *ConfigHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/config/")
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.GET("get", h.Get)
|
||||
group.GET("license", h.License)
|
||||
}
|
||||
// 无需授权的接口
|
||||
group.GET("get", h.Get)
|
||||
group.GET("license", h.License)
|
||||
}
|
||||
|
||||
// Get 获取指定的系统配置
|
||||
|
||||
@@ -58,6 +58,7 @@ func NewFunctionHandler(
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *FunctionHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/function/")
|
||||
group.GET("list", h.List)
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
@@ -66,7 +67,6 @@ func (h *FunctionHandler) RegisterRoutes() {
|
||||
group.POST("zaobao", h.ZaoBao)
|
||||
group.POST("dalle3", h.Dall3)
|
||||
group.POST("websearch", h.WebSearch)
|
||||
group.GET("list", h.List)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ package handler
|
||||
|
||||
import (
|
||||
"geekai/core"
|
||||
"geekai/core/middleware"
|
||||
"geekai/store/model"
|
||||
"geekai/store/vo"
|
||||
"geekai/utils"
|
||||
@@ -30,12 +29,7 @@ func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler {
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *MenuHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/menu/")
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.GET("list", h.List)
|
||||
}
|
||||
group.GET("list", h.List)
|
||||
}
|
||||
|
||||
// List 数据列表
|
||||
|
||||
@@ -35,12 +35,12 @@ func NewNetHandler(app *core.AppServer, db *gorm.DB, manager *oss.UploaderManage
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *NetHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/upload/")
|
||||
group := h.App.Engine.Group("/api/upload")
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.POST("upload", h.Upload)
|
||||
group.POST("", h.Upload)
|
||||
group.POST("list", h.List)
|
||||
group.GET("remove", h.Remove)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/midware"
|
||||
"geekai/core/middleware"
|
||||
"geekai/core/types"
|
||||
"geekai/service"
|
||||
"geekai/service/payment"
|
||||
@@ -81,7 +81,7 @@ func (h *PaymentHandler) RegisterRoutes() {
|
||||
rg.POST("notify/wechat", h.WechatPayNotify)
|
||||
|
||||
// 需要用户登录的接口
|
||||
rg.Use(midware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
rg.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
rg.POST("create", h.Pay)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package handler
|
||||
|
||||
import (
|
||||
"geekai/core"
|
||||
"geekai/core/middleware"
|
||||
"geekai/core/types"
|
||||
"geekai/service"
|
||||
"geekai/service/sms"
|
||||
@@ -48,12 +47,8 @@ func NewSmsHandler(
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *SmsHandler) RegisterRoutes() {
|
||||
group := h.App.Engine.Group("/api/sms/")
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.POST("code", h.SendCode)
|
||||
}
|
||||
// 无需授权的接口
|
||||
group.POST("code", h.SendCode)
|
||||
}
|
||||
|
||||
// SendCode 发送验证码
|
||||
|
||||
@@ -68,12 +68,12 @@ func (h *UserHandler) RegisterRoutes() {
|
||||
group.POST("login", h.Login)
|
||||
group.POST("resetPass", h.ResetPass)
|
||||
group.GET("clogin", h.CLogin)
|
||||
group.GET("logout", h.Logout)
|
||||
group.GET("clogin/callback", h.CLoginCallback)
|
||||
|
||||
// 需要用户授权的接口
|
||||
group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
|
||||
{
|
||||
group.GET("logout", h.Logout)
|
||||
group.GET("session", h.Session)
|
||||
group.GET("profile", h.Profile)
|
||||
group.POST("profile/update", h.ProfileUpdate)
|
||||
|
||||
14
api/main.go
14
api/main.go
@@ -112,12 +112,6 @@ func main() {
|
||||
return xdbFS
|
||||
}),
|
||||
|
||||
// 数据修复
|
||||
fx.Provide(service.NewDataFixService),
|
||||
fx.Invoke(func(s *core.AppServer, dfs *service.DataFixService) {
|
||||
dfs.FixData()
|
||||
}),
|
||||
|
||||
// 创建 Ip2Region 查询对象
|
||||
fx.Provide(func() (*xdb.Searcher, error) {
|
||||
file, err := xdbFS.Open("res/ip2region.xdb")
|
||||
@@ -151,11 +145,9 @@ func main() {
|
||||
fx.Provide(handler.NewJimengHandler),
|
||||
|
||||
fx.Provide(service.NewConfigService),
|
||||
fx.Provide(service.NewConfigMigrationService),
|
||||
fx.Invoke(func(migrationService *service.ConfigMigrationService, config *types.AppConfig, redisClient *redis.Client) {
|
||||
if err := migrationService.MigrateFromConfig(config); err != nil {
|
||||
logger.Errorf("配置迁移失败: %v", err)
|
||||
}
|
||||
fx.Provide(service.NewMigrationService),
|
||||
fx.Invoke(func(migrationService *service.MigrationService) {
|
||||
migrationService.StartMigrate()
|
||||
}),
|
||||
|
||||
// 管理后台控制器
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"geekai/store/model"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DataFixService struct {
|
||||
db *gorm.DB
|
||||
redis *redis.Client
|
||||
}
|
||||
|
||||
func NewDataFixService(db *gorm.DB, redis *redis.Client) *DataFixService {
|
||||
return &DataFixService{db: db, redis: redis}
|
||||
}
|
||||
|
||||
func (s *DataFixService) FixData() {
|
||||
s.FixColumn()
|
||||
}
|
||||
|
||||
// 字段修正
|
||||
func (s *DataFixService) FixColumn() {
|
||||
// 订单字段整理
|
||||
if s.db.Migrator().HasColumn(&model.Order{}, "pay_type") {
|
||||
s.db.Migrator().RenameColumn(&model.Order{}, "pay_type", "channel")
|
||||
}
|
||||
if !s.db.Migrator().HasColumn(&model.Order{}, "check") {
|
||||
s.db.Migrator().AddColumn(&model.Order{}, "checked")
|
||||
}
|
||||
|
||||
// 重命名 config 表字段
|
||||
if s.db.Migrator().HasColumn(&model.Config{}, "config_json") {
|
||||
s.db.Migrator().RenameColumn(&model.Config{}, "config_json", "value")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.Config{}, "marker") {
|
||||
s.db.Migrator().RenameColumn(&model.Config{}, "marker", "name")
|
||||
}
|
||||
if s.db.Migrator().HasIndex(&model.Config{}, "idx_chatgpt_configs_key") {
|
||||
s.db.Migrator().DropIndex(&model.Config{}, "idx_chatgpt_configs_key")
|
||||
}
|
||||
if s.db.Migrator().HasIndex(&model.Config{}, "marker") {
|
||||
s.db.Migrator().DropIndex(&model.Config{}, "marker")
|
||||
}
|
||||
|
||||
// 手动删除字段
|
||||
if s.db.Migrator().HasColumn(&model.Order{}, "deleted_at") {
|
||||
s.db.Migrator().DropColumn(&model.Order{}, "deleted_at")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatItem{}, "deleted_at") {
|
||||
s.db.Migrator().DropColumn(&model.ChatItem{}, "deleted_at")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatMessage{}, "deleted_at") {
|
||||
s.db.Migrator().DropColumn(&model.ChatMessage{}, "deleted_at")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.User{}, "chat_config") {
|
||||
s.db.Migrator().DropColumn(&model.User{}, "chat_config")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatModel{}, "category") {
|
||||
s.db.Migrator().DropColumn(&model.ChatModel{}, "category")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") {
|
||||
s.db.Migrator().DropColumn(&model.ChatModel{}, "description")
|
||||
}
|
||||
}
|
||||
@@ -10,37 +10,35 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/types"
|
||||
"geekai/store"
|
||||
"geekai/store/model"
|
||||
"geekai/utils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type LicenseService struct {
|
||||
levelDB *store.LevelDB
|
||||
license *types.License
|
||||
urlWhiteList []string
|
||||
machineId string
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewLicenseService(server *core.AppServer, levelDB *store.LevelDB) *LicenseService {
|
||||
var license types.License
|
||||
func NewLicenseService(sysConfig *types.SystemConfig, db *gorm.DB) *LicenseService {
|
||||
var machineId string
|
||||
err := levelDB.Get(types.LicenseKey, &license)
|
||||
logger.Infof("License: %+v", server.SysConfig)
|
||||
info, err := host.Info()
|
||||
if err == nil {
|
||||
machineId = info.HostID
|
||||
}
|
||||
logger.Infof("License: %+v", license)
|
||||
logger.Infof("License: %+v", sysConfig.License)
|
||||
return &LicenseService{
|
||||
levelDB: levelDB,
|
||||
license: &license,
|
||||
license: &sysConfig.License,
|
||||
machineId: machineId,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,10 +86,13 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error {
|
||||
ExpiredAt: res.Data.ExpiredAt,
|
||||
IsActive: true,
|
||||
}
|
||||
err = s.levelDB.Put(types.LicenseKey, s.license)
|
||||
|
||||
// 保存 License 到数据库
|
||||
err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("保存许可证书失败:%v", err)
|
||||
return fmt.Errorf("保存 License 到数据库失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -173,6 +174,13 @@ func (s *LicenseService) fetchUrlWhiteList() ([]string, error) {
|
||||
|
||||
// GetLicense 获取许可信息
|
||||
func (s *LicenseService) GetLicense() *types.License {
|
||||
if s.license == nil {
|
||||
var config model.Config
|
||||
s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).First(&config)
|
||||
if config.Value != "" {
|
||||
utils.JsonDecode(config.Value, &s.license)
|
||||
}
|
||||
}
|
||||
return s.license
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"geekai/core/types"
|
||||
"geekai/store"
|
||||
"geekai/store/model"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
@@ -24,27 +25,103 @@ const (
|
||||
MigrationCompleted = "completed"
|
||||
)
|
||||
|
||||
// ConfigMigrationService 配置迁移服务
|
||||
type ConfigMigrationService struct {
|
||||
// MigrationService 配置迁移服务
|
||||
type MigrationService struct {
|
||||
db *gorm.DB
|
||||
redisClient *redis.Client
|
||||
appConfig *types.AppConfig
|
||||
levelDB *store.LevelDB
|
||||
}
|
||||
|
||||
func NewConfigMigrationService(db *gorm.DB, redisClient *redis.Client) *ConfigMigrationService {
|
||||
return &ConfigMigrationService{
|
||||
func NewMigrationService(db *gorm.DB, redisClient *redis.Client, appConfig *types.AppConfig, levelDB *store.LevelDB) *MigrationService {
|
||||
return &MigrationService{
|
||||
db: db,
|
||||
redisClient: redisClient,
|
||||
appConfig: appConfig,
|
||||
levelDB: levelDB,
|
||||
}
|
||||
}
|
||||
|
||||
// MigrateFromConfig 从 config.toml 迁移配置到数据库(仅首次启动时执行)
|
||||
func (s *ConfigMigrationService) MigrateFromConfig(config *types.AppConfig) error {
|
||||
// 检查是否已经迁移过
|
||||
if s.isMigrationCompleted() {
|
||||
logger.Info("配置迁移已完成,跳过迁移")
|
||||
return nil
|
||||
func (s *MigrationService) StartMigrate() {
|
||||
go func() {
|
||||
s.MigrateConfig(s.appConfig)
|
||||
s.TableMigration()
|
||||
s.MigrateLicense()
|
||||
}()
|
||||
}
|
||||
|
||||
// 迁移 License
|
||||
func (s *MigrationService) MigrateLicense() {
|
||||
key := "migrate:license"
|
||||
if s.redisClient.Get(context.Background(), key).Val() == "1" {
|
||||
logger.Info("License 已迁移,跳过迁移")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("开始迁移 License...")
|
||||
var license types.License
|
||||
err := s.levelDB.Get(types.LicenseKey, &license)
|
||||
if err != nil {
|
||||
logger.Errorf("迁移 License 失败: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Infof("迁移 License: %+v", license)
|
||||
if err := s.saveConfig(types.ConfigKeyLicense, license); err != nil {
|
||||
logger.Errorf("迁移 License 失败: %v", err)
|
||||
return
|
||||
}
|
||||
logger.Info("迁移 License 完成")
|
||||
s.redisClient.Set(context.Background(), key, "1", 0)
|
||||
}
|
||||
|
||||
// 数据表迁移
|
||||
func (s *MigrationService) TableMigration() {
|
||||
// 订单字段整理
|
||||
if s.db.Migrator().HasColumn(&model.Order{}, "pay_type") {
|
||||
s.db.Migrator().RenameColumn(&model.Order{}, "pay_type", "channel")
|
||||
}
|
||||
if !s.db.Migrator().HasColumn(&model.Order{}, "checked") {
|
||||
s.db.Migrator().AddColumn(&model.Order{}, "checked")
|
||||
}
|
||||
|
||||
// 重命名 config 表字段
|
||||
if s.db.Migrator().HasColumn(&model.Config{}, "config_json") {
|
||||
s.db.Migrator().RenameColumn(&model.Config{}, "config_json", "value")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.Config{}, "marker") {
|
||||
s.db.Migrator().RenameColumn(&model.Config{}, "marker", "name")
|
||||
}
|
||||
if s.db.Migrator().HasIndex(&model.Config{}, "idx_chatgpt_configs_key") {
|
||||
s.db.Migrator().DropIndex(&model.Config{}, "idx_chatgpt_configs_key")
|
||||
}
|
||||
if s.db.Migrator().HasIndex(&model.Config{}, "marker") {
|
||||
s.db.Migrator().DropIndex(&model.Config{}, "marker")
|
||||
}
|
||||
|
||||
// 手动删除字段
|
||||
if s.db.Migrator().HasColumn(&model.Order{}, "deleted_at") {
|
||||
s.db.Migrator().DropColumn(&model.Order{}, "deleted_at")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatItem{}, "deleted_at") {
|
||||
s.db.Migrator().DropColumn(&model.ChatItem{}, "deleted_at")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatMessage{}, "deleted_at") {
|
||||
s.db.Migrator().DropColumn(&model.ChatMessage{}, "deleted_at")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.User{}, "chat_config") {
|
||||
s.db.Migrator().DropColumn(&model.User{}, "chat_config")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatModel{}, "category") {
|
||||
s.db.Migrator().DropColumn(&model.ChatModel{}, "category")
|
||||
}
|
||||
if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") {
|
||||
s.db.Migrator().DropColumn(&model.ChatModel{}, "description")
|
||||
}
|
||||
}
|
||||
|
||||
// 迁移配置数据
|
||||
func (s *MigrationService) MigrateConfig(config *types.AppConfig) error {
|
||||
|
||||
logger.Info("开始迁移配置到数据库...")
|
||||
|
||||
// 迁移支付配置
|
||||
@@ -65,36 +142,12 @@ func (s *ConfigMigrationService) MigrateFromConfig(config *types.AppConfig) erro
|
||||
return err
|
||||
}
|
||||
|
||||
// 标记迁移完成
|
||||
if err := s.markMigrationCompleted(); err != nil {
|
||||
logger.Errorf("标记迁移完成失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("配置迁移完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查是否已经迁移完成
|
||||
func (s *ConfigMigrationService) isMigrationCompleted() bool {
|
||||
ctx := context.Background()
|
||||
status, err := s.redisClient.Get(ctx, MigrationStatusKey).Result()
|
||||
if err != nil {
|
||||
// Redis中没有找到标志,说明未迁移过
|
||||
return false
|
||||
}
|
||||
return status == MigrationCompleted
|
||||
}
|
||||
|
||||
// 标记迁移完成
|
||||
func (s *ConfigMigrationService) markMigrationCompleted() error {
|
||||
ctx := context.Background()
|
||||
// 设置迁移完成标志,永不过期
|
||||
return s.redisClient.Set(ctx, MigrationStatusKey, MigrationCompleted, 0).Err()
|
||||
}
|
||||
|
||||
// 迁移支付配置
|
||||
func (s *ConfigMigrationService) migratePaymentConfig(config *types.AppConfig) error {
|
||||
func (s *MigrationService) migratePaymentConfig(config *types.AppConfig) error {
|
||||
|
||||
paymentConfig := types.PaymentConfig{
|
||||
Alipay: config.AlipayConfig,
|
||||
@@ -109,7 +162,7 @@ func (s *ConfigMigrationService) migratePaymentConfig(config *types.AppConfig) e
|
||||
}
|
||||
|
||||
// 迁移存储配置
|
||||
func (s *ConfigMigrationService) migrateStorageConfig(config *types.AppConfig) error {
|
||||
func (s *MigrationService) migrateStorageConfig(config *types.AppConfig) error {
|
||||
|
||||
ossConfig := types.OSSConfig{
|
||||
Active: config.OSS.Active,
|
||||
@@ -122,7 +175,7 @@ func (s *ConfigMigrationService) migrateStorageConfig(config *types.AppConfig) e
|
||||
}
|
||||
|
||||
// 迁移通信配置
|
||||
func (s *ConfigMigrationService) migrateCommunicationConfig(config *types.AppConfig) error {
|
||||
func (s *MigrationService) migrateCommunicationConfig(config *types.AppConfig) error {
|
||||
// SMTP配置
|
||||
smtpConfig := map[string]any{
|
||||
"use_tls": config.SmtpConfig.UseTls,
|
||||
@@ -156,7 +209,7 @@ func (s *ConfigMigrationService) migrateCommunicationConfig(config *types.AppCon
|
||||
}
|
||||
|
||||
// 保存配置到数据库
|
||||
func (s *ConfigMigrationService) saveConfig(key string, config any) error {
|
||||
func (s *MigrationService) saveConfig(key string, config any) error {
|
||||
// 检查是否已存在
|
||||
var existingConfig model.Config
|
||||
if err := s.db.Where("name", key).First(&existingConfig).Error; err == nil {
|
||||
@@ -34,6 +34,7 @@
|
||||
<button
|
||||
class="w-full h-12 rounded-xl text-base font-medium text-white bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg active:translate-y-0 shadow-md"
|
||||
@click="submitLogin"
|
||||
type="button"
|
||||
>
|
||||
{{ loading ? '登录中...' : '登 录' }}
|
||||
</button>
|
||||
@@ -210,6 +211,7 @@
|
||||
<button
|
||||
class="w-full h-12 rounded-xl text-base font-medium text-white bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg active:translate-y-0 shadow-md"
|
||||
@click="submitRegister"
|
||||
type="button"
|
||||
>
|
||||
{{ loading ? '注册中...' : '注 册' }}
|
||||
</button>
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/**
|
||||
* 设备检测工具函数
|
||||
* 用于判断当前设备类型,支持PC端和移动端的智能识别
|
||||
*/
|
||||
|
||||
/**
|
||||
* 检测设备类型
|
||||
* @returns {string} 'mobile' | 'desktop'
|
||||
*/
|
||||
export const detectDevice = () => {
|
||||
const userAgent = navigator.userAgent.toLowerCase()
|
||||
|
||||
// 移动设备关键词检测
|
||||
const mobileKeywords = [
|
||||
'mobile',
|
||||
'android',
|
||||
'iphone',
|
||||
'ipad',
|
||||
'phone',
|
||||
'blackberry',
|
||||
'opera mini',
|
||||
'windows phone',
|
||||
'iemobile',
|
||||
]
|
||||
|
||||
// 平板设备关键词检测
|
||||
const tabletKeywords = ['tablet', 'ipad', 'playbook', 'silk', 'kindle']
|
||||
|
||||
// 检查是否为移动设备
|
||||
const isMobile = mobileKeywords.some((keyword) => userAgent.includes(keyword))
|
||||
|
||||
// 检查是否为平板设备
|
||||
const isTablet = tabletKeywords.some((keyword) => userAgent.includes(keyword))
|
||||
|
||||
// 检查屏幕尺寸
|
||||
const screenWidth = window.innerWidth
|
||||
const screenHeight = window.innerHeight
|
||||
|
||||
// 移动设备判断逻辑
|
||||
if (isMobile || isTablet || screenWidth <= 768) {
|
||||
return 'mobile'
|
||||
}
|
||||
|
||||
return 'desktop'
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为移动设备
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isMobileDevice = () => {
|
||||
return detectDevice() === 'mobile'
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否为桌面设备
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isDesktopDevice = () => {
|
||||
return detectDevice() === 'desktop'
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备跳转路径
|
||||
* @param {string} deviceType - 设备类型
|
||||
* @param {string} defaultPath - 默认路径
|
||||
* @returns {string} 跳转路径
|
||||
*/
|
||||
export const getDeviceRedirectPath = (deviceType, defaultPath = '/') => {
|
||||
if (deviceType === 'mobile') {
|
||||
return '/mobile'
|
||||
}
|
||||
return defaultPath
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前设备获取跳转路径
|
||||
* @param {string} defaultPath - 默认路径
|
||||
* @returns {string} 跳转路径
|
||||
*/
|
||||
export const getCurrentDeviceRedirectPath = (defaultPath = '/') => {
|
||||
const deviceType = detectDevice()
|
||||
return getDeviceRedirectPath(deviceType, defaultPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测屏幕尺寸
|
||||
* @returns {object} { width, height, isSmall, isMedium, isLarge }
|
||||
*/
|
||||
export const getScreenInfo = () => {
|
||||
const width = window.innerWidth
|
||||
const height = window.innerHeight
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
isSmall: width <= 768,
|
||||
isMedium: width > 768 && width <= 1024,
|
||||
isLarge: width > 1024,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测浏览器类型
|
||||
* @returns {string} 浏览器类型
|
||||
*/
|
||||
export const detectBrowser = () => {
|
||||
const userAgent = navigator.userAgent.toLowerCase()
|
||||
|
||||
if (userAgent.includes('chrome')) return 'chrome'
|
||||
if (userAgent.includes('firefox')) return 'firefox'
|
||||
if (userAgent.includes('safari') && !userAgent.includes('chrome')) return 'safari'
|
||||
if (userAgent.includes('edge')) return 'edge'
|
||||
if (userAgent.includes('opera')) return 'opera'
|
||||
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测操作系统
|
||||
* @returns {string} 操作系统类型
|
||||
*/
|
||||
export const detectOS = () => {
|
||||
const userAgent = navigator.userAgent.toLowerCase()
|
||||
|
||||
if (userAgent.includes('windows')) return 'windows'
|
||||
if (userAgent.includes('mac')) return 'macos'
|
||||
if (userAgent.includes('linux')) return 'linux'
|
||||
if (userAgent.includes('android')) return 'android'
|
||||
if (userAgent.includes('ios')) return 'ios'
|
||||
|
||||
return 'unknown'
|
||||
}
|
||||
@@ -30,12 +30,7 @@
|
||||
</div>
|
||||
|
||||
<div class="login-content">
|
||||
<login-dialog
|
||||
:show="true"
|
||||
@hide="handleLoginHide"
|
||||
@success="handleLoginSuccess"
|
||||
ref="loginDialogRef"
|
||||
/>
|
||||
<login-dialog :show="true" @success="handleLoginSuccess" ref="loginDialogRef" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,7 +40,7 @@
|
||||
<script setup>
|
||||
import LoginDialog from '@/components/LoginDialog.vue'
|
||||
import { setUserToken } from '@/store/session'
|
||||
import { getCurrentDeviceRedirectPath } from '@/utils/device'
|
||||
import { isMobile } from '@/utils/libs'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -58,16 +53,13 @@ if (token) {
|
||||
router.push('/chat')
|
||||
}
|
||||
|
||||
// 处理登录弹窗隐藏
|
||||
const handleLoginHide = () => {
|
||||
const redirectPath = getCurrentDeviceRedirectPath()
|
||||
router.push(redirectPath)
|
||||
}
|
||||
|
||||
// 处理登录成功
|
||||
const handleLoginSuccess = () => {
|
||||
const redirectPath = getCurrentDeviceRedirectPath()
|
||||
router.push(redirectPath)
|
||||
if (isMobile()) {
|
||||
router.push('/mobile')
|
||||
} else {
|
||||
router.push('/chat')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
:show="true"
|
||||
active="register"
|
||||
:inviteCode="inviteCode"
|
||||
@hide="handleRegisterHide"
|
||||
@success="handleRegisterSuccess"
|
||||
ref="loginDialogRef"
|
||||
/>
|
||||
@@ -46,7 +45,7 @@
|
||||
|
||||
<script setup>
|
||||
import LoginDialog from '@/components/LoginDialog.vue'
|
||||
import { getCurrentDeviceRedirectPath } from '@/utils/device'
|
||||
import { isMobile } from '@/utils/libs'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -54,16 +53,13 @@ const router = useRouter()
|
||||
const loginDialogRef = ref(null)
|
||||
const inviteCode = ref(router.currentRoute.value.query.invite_code || '')
|
||||
|
||||
// 处理注册弹窗隐藏
|
||||
const handleRegisterHide = () => {
|
||||
const redirectPath = getCurrentDeviceRedirectPath()
|
||||
router.push(redirectPath)
|
||||
}
|
||||
|
||||
// 处理注册成功
|
||||
const handleRegisterSuccess = () => {
|
||||
const redirectPath = getCurrentDeviceRedirectPath()
|
||||
router.push(redirectPath)
|
||||
if (isMobile()) {
|
||||
router.push('/mobile')
|
||||
} else {
|
||||
router.push('/chat')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user