diff --git a/api/auth/login.go b/api/auth/login.go index c4a179d..0a9727a 100644 --- a/api/auth/login.go +++ b/api/auth/login.go @@ -228,7 +228,7 @@ func login(x *vigo.X, req *LoginRequest) (*AuthResponse, error) { Type: "access", DeviceInfo: x.Request.UserAgent(), IP: x.GetRemoteIP(), - ExpiresAt: time.Now().Add(cfg.Config.JWT.AccessExpiry), + ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry), } cfg.DB().Create(session) @@ -323,7 +323,7 @@ func refresh(x *vigo.X, req *RefreshRequest) (*AuthResponse, error) { Type: "access", DeviceInfo: x.Request.UserAgent(), IP: x.GetRemoteIP(), - ExpiresAt: time.Now().Add(cfg.Config.JWT.AccessExpiry), + ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry), } cfg.DB().Create(session) diff --git a/api/auth/thirdparty.go b/api/auth/thirdparty.go index 0c36223..a1ee6c4 100644 --- a/api/auth/thirdparty.go +++ b/api/auth/thirdparty.go @@ -452,7 +452,7 @@ func exchangeGeneric(provider models.OAuthProvider, code string) (*ThirdPartyUse // 解密 ClientSecret clientSecret := provider.ClientSecret if clientSecret != "" { - decrypted, err := cfg.Config.Key.Decrypt(clientSecret) + decrypted, err := cfg.Global.Key.Decrypt(clientSecret) if err == nil { clientSecret = decrypted } diff --git a/api/oauth/providers/create.go b/api/oauth/providers/create.go index eba7727..f61ace4 100644 --- a/api/oauth/providers/create.go +++ b/api/oauth/providers/create.go @@ -30,7 +30,7 @@ func create(x *vigo.X, req *models.OAuthProvider) (*models.OAuthProvider, error) // 加密 ClientSecret if req.ClientSecret != "" { - encrypted, err := cfg.Config.Key.Encrypt(req.ClientSecret) + encrypted, err := cfg.Global.Key.Encrypt(req.ClientSecret) if err != nil { return nil, vigo.ErrInternalServer.WithString("failed to encrypt client secret") } diff --git a/api/oauth/providers/update.go b/api/oauth/providers/update.go index 1027798..f204e3d 100644 --- a/api/oauth/providers/update.go +++ b/api/oauth/providers/update.go @@ -39,7 +39,7 @@ func update(x *vigo.X, req *UpdateRequest) (*models.OAuthProvider, error) { // 如果提供了新的 ClientSecret,则加密;否则保留原值 if req.OAuthProvider.ClientSecret != "" { - encrypted, err := cfg.Config.Key.Encrypt(req.OAuthProvider.ClientSecret) + encrypted, err := cfg.Global.Key.Encrypt(req.OAuthProvider.ClientSecret) if err != nil { return nil, vigo.ErrInternalServer.WithString("failed to encrypt client secret") } diff --git a/cfg/cfg.go b/cfg/cfg.go index 1fcc375..4be60b2 100644 --- a/cfg/cfg.go +++ b/cfg/cfg.go @@ -50,8 +50,8 @@ type InitAdminConfig struct { Email string `json:"email" usage:"管理员邮箱"` } -// Config 全局配置实例 -var Config = &Options{ +// Global全局配置实例 +var Global = &Options{ DB: config.Database{ Type: "mysql", DSN: "root:123456@tcp(127.0.0.1:3306)/vbase?charset=utf8&parseTime=True&loc=Local", @@ -74,4 +74,4 @@ var Config = &Options{ }, } -var DB = Config.DB.Client +var DB = Global.DB.Client diff --git a/cli/main.go b/cli/main.go index e7c22be..e49cb5e 100644 --- a/cli/main.go +++ b/cli/main.go @@ -9,50 +9,8 @@ package main import ( "github.com/veypi/vbase" - "github.com/veypi/vbase/cfg" - "github.com/veypi/vbase/models" - "github.com/veypi/vigo" - "github.com/veypi/vigo/contrib/event" - "github.com/veypi/vigo/flags" - "github.com/veypi/vigo/logv" ) -var cliOpts = &struct { - Host string `json:"host"` - Port int `json:"port" short:"p"` - *cfg.Options -}{ - Host: "0.0.0.0", - Port: 4000, - Options: cfg.Config, -} - -var ( - cmdMain = flags.New("app", "the backend server of app", cliOpts) - cmdDB = cmdMain.SubCommand("db", "database operations") -) - -func init() { - cmdMain.Command = runWeb - cmdDB.SubCommand("migrate", "migrate database").Command = models.Migrate - cmdDB.SubCommand("drop", "drop database").Command = models.Drop -} - func main() { - cmdMain.Parse() - err := cmdMain.Run() - if err != nil { - logv.Warn().Msg(err.Error()) - } -} - -func runWeb() error { - models.Migrate() - event.Start() - server, err := vigo.New(vigo.WithHost(cliOpts.Host), vigo.WithPort(cliOpts.Port)) - if err != nil { - return err - } - server.SetRouter(vbase.Router) - return server.Run() + panic(vbase.App.Run()) } diff --git a/init.go b/init.go index c9d6335..a47a1e6 100644 --- a/init.go +++ b/init.go @@ -12,6 +12,7 @@ import ( "github.com/veypi/vbase/api" "github.com/veypi/vbase/auth" "github.com/veypi/vbase/cfg" + "github.com/veypi/vbase/models" "github.com/veypi/vhtml" vhtmlui "github.com/veypi/vhtml-ui" "github.com/veypi/vigo" @@ -21,7 +22,6 @@ var Router = vigo.NewRouter() var ( Auth = auth.Factory - Config = cfg.Config AuthMiddleware = auth.AuthMiddleware ) @@ -34,3 +34,9 @@ func init() { Router.Extend("vhtml", vhtml.Router) vhtml.WrapUI(Router, uifs) } + +var App = vigo.New("vbase", Router, cfg.Global, Init) + +func Init() error { + return models.Migrate() +} diff --git a/libs/cache/cache.go b/libs/cache/cache.go index 2cfc43e..5fc9387 100644 --- a/libs/cache/cache.go +++ b/libs/cache/cache.go @@ -23,14 +23,14 @@ var ( // Init 初始化Redis连接 func Init() error { - if cfg.Config.Redis.Addr == "" || cfg.Config.Redis.Addr == "memory" { + if cfg.Global.Redis.Addr == "" || cfg.Global.Redis.Addr == "memory" { return nil } Client = redis.NewClient(&redis.Options{ - Addr: cfg.Config.Redis.Addr, - Password: cfg.Config.Redis.Password, - DB: cfg.Config.Redis.DB, + Addr: cfg.Global.Redis.Addr, + Password: cfg.Global.Redis.Password, + DB: cfg.Global.Redis.DB, }) if err := Client.Ping(Ctx).Err(); err != nil { @@ -42,7 +42,7 @@ func Init() error { // IsEnabled 是否启用缓存 func IsEnabled() bool { - return cfg.Config.Redis.Addr != "" && cfg.Config.Redis.Addr != "memory" && Client != nil + return cfg.Global.Redis.Addr != "" && cfg.Global.Redis.Addr != "memory" && Client != nil } // Get 获取字符串值 diff --git a/libs/jwt/jwt.go b/libs/jwt/jwt.go index 01a86c1..0104596 100644 --- a/libs/jwt/jwt.go +++ b/libs/jwt/jwt.go @@ -68,7 +68,7 @@ func GenerateTokenPair(userID, username, nickname, avatar, email string, orgs [] AccessToken: accessToken, RefreshToken: refreshToken, TokenType: "Bearer", - ExpiresIn: int(cfg.Config.JWT.AccessExpiry.Seconds()), + ExpiresIn: int(cfg.Global.JWT.AccessExpiry.Seconds()), }, nil } @@ -78,12 +78,12 @@ func GenerateAccessToken(userID, username, nickname, avatar, email string, orgs claims := Claims{ RegisteredClaims: jwt.RegisteredClaims{ ID: uuid.New().String(), // jti - Issuer: cfg.Config.JWT.Issuer, + Issuer: cfg.Global.JWT.Issuer, Subject: userID, Audience: jwt.ClaimStrings{"vbase"}, IssuedAt: jwt.NewNumericDate(now), NotBefore: jwt.NewNumericDate(now), - ExpiresAt: jwt.NewNumericDate(now.Add(cfg.Config.JWT.AccessExpiry)), + ExpiresAt: jwt.NewNumericDate(now.Add(cfg.Global.JWT.AccessExpiry)), }, UserID: userID, Username: username, @@ -95,7 +95,7 @@ func GenerateAccessToken(userID, username, nickname, avatar, email string, orgs } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(cfg.Config.JWT.Secret)) + return token.SignedString([]byte(cfg.Global.JWT.Secret)) } // GenerateRefreshToken 生成刷新令牌 @@ -104,17 +104,17 @@ func GenerateRefreshToken(userID string) (string, error) { claims := Claims{ RegisteredClaims: jwt.RegisteredClaims{ ID: uuid.New().String(), - Issuer: cfg.Config.JWT.Issuer, + Issuer: cfg.Global.JWT.Issuer, Subject: userID, IssuedAt: jwt.NewNumericDate(now), - ExpiresAt: jwt.NewNumericDate(now.Add(cfg.Config.JWT.RefreshExpiry)), + ExpiresAt: jwt.NewNumericDate(now.Add(cfg.Global.JWT.RefreshExpiry)), }, UserID: userID, Type: "refresh", } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(cfg.Config.JWT.Secret)) + return token.SignedString([]byte(cfg.Global.JWT.Secret)) } // ParseToken 解析Token @@ -123,9 +123,8 @@ func ParseToken(tokenString string) (*Claims, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } - return []byte(cfg.Config.JWT.Secret), nil + return []byte(cfg.Global.JWT.Secret), nil }) - if err != nil { if errors.Is(err, jwt.ErrTokenExpired) { return nil, ErrExpiredToken diff --git a/models/init.go b/models/init.go index 3983f0e..006ec8e 100644 --- a/models/init.go +++ b/models/init.go @@ -72,7 +72,7 @@ func initAdminUser() error { } // 检查 init_admin 配置 - adminCfg := cfg.Config.InitAdmin + adminCfg := cfg.Global.InitAdmin if adminCfg.Username == "" || adminCfg.Password == "" { // 未配置初始管理员,跳过 return nil diff --git a/tests/main_test.go b/tests/main_test.go index 2650116..64e2e61 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -44,12 +44,12 @@ func TestMain(m *testing.M) { func setup() { // Configure for testing // Use in-memory SQLite database - cfg.Config.DB = config.Database{ + cfg.Global.DB = config.Database{ Type: "sqlite", DSN: TestDBFile, } // Use mock/memory Redis - cfg.Config.Redis = config.Redis{ + cfg.Global.Redis = config.Redis{ Addr: "memory", } // Initialize DB connection and run migrations diff --git a/ui/env.js b/ui/env.js index 716bbcb..073ab60 100644 --- a/ui/env.js +++ b/ui/env.js @@ -11,7 +11,8 @@ export default async ($env) => { } // Initialize VBase Service - const vbase = new VBase(''); // Relative path + const vbase = new VBase('vb', '/'); // Relative path + console.log(vbase) $env.$vbase = vbase; // Wrap Axios diff --git a/ui/page/auth/login.html b/ui/page/auth/login.html index 5ff8172..2639421 100644 --- a/ui/page/auth/login.html +++ b/ui/page/auth/login.html @@ -41,7 +41,7 @@
diff --git a/ui/vbase.js b/ui/vbase.js index f5d80a4..e10e64e 100644 --- a/ui/vbase.js +++ b/ui/vbase.js @@ -1,8 +1,14 @@ class VBase { - constructor(baseURL, scope) { - if (!baseURL) throw new Error('VBase: baseURL is required'); + constructor(scope, baseURL) { if (!scope) throw new Error('VBase: scope is required'); + if (!baseURL) throw new Error('VBase: baseURL is required'); + if (!baseURL) { + baseURL = '' + } + if (baseURL === '' || baseURL === '/') { + baseURL = window.location.origin + } this.baseURL = baseURL; this.scope = scope;