refactor: Rename Config to Global and simplify app initialization

- Rename cfg.Config to cfg.Global for consistency
    - Simplify cli/main.go to use vbase.App.Run() pattern
    - Update init.go to create app with vigo.New and Init function
    - Update all references from cfg.Config to cfg.Global across api, libs, models, and tests
    - Fix VBase constructor parameter order in ui/vbase.js
    - Update ui/env.js to use new VBase('vb', '/') initialization
master
veypi 5 days ago
parent 0b22d2c2c8
commit df0f7f047a

@ -228,7 +228,7 @@ func login(x *vigo.X, req *LoginRequest) (*AuthResponse, error) {
Type: "access", Type: "access",
DeviceInfo: x.Request.UserAgent(), DeviceInfo: x.Request.UserAgent(),
IP: x.GetRemoteIP(), IP: x.GetRemoteIP(),
ExpiresAt: time.Now().Add(cfg.Config.JWT.AccessExpiry), ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry),
} }
cfg.DB().Create(session) cfg.DB().Create(session)
@ -323,7 +323,7 @@ func refresh(x *vigo.X, req *RefreshRequest) (*AuthResponse, error) {
Type: "access", Type: "access",
DeviceInfo: x.Request.UserAgent(), DeviceInfo: x.Request.UserAgent(),
IP: x.GetRemoteIP(), IP: x.GetRemoteIP(),
ExpiresAt: time.Now().Add(cfg.Config.JWT.AccessExpiry), ExpiresAt: time.Now().Add(cfg.Global.JWT.AccessExpiry),
} }
cfg.DB().Create(session) cfg.DB().Create(session)

@ -452,7 +452,7 @@ func exchangeGeneric(provider models.OAuthProvider, code string) (*ThirdPartyUse
// 解密 ClientSecret // 解密 ClientSecret
clientSecret := provider.ClientSecret clientSecret := provider.ClientSecret
if clientSecret != "" { if clientSecret != "" {
decrypted, err := cfg.Config.Key.Decrypt(clientSecret) decrypted, err := cfg.Global.Key.Decrypt(clientSecret)
if err == nil { if err == nil {
clientSecret = decrypted clientSecret = decrypted
} }

@ -30,7 +30,7 @@ func create(x *vigo.X, req *models.OAuthProvider) (*models.OAuthProvider, error)
// 加密 ClientSecret // 加密 ClientSecret
if req.ClientSecret != "" { if req.ClientSecret != "" {
encrypted, err := cfg.Config.Key.Encrypt(req.ClientSecret) encrypted, err := cfg.Global.Key.Encrypt(req.ClientSecret)
if err != nil { if err != nil {
return nil, vigo.ErrInternalServer.WithString("failed to encrypt client secret") return nil, vigo.ErrInternalServer.WithString("failed to encrypt client secret")
} }

@ -39,7 +39,7 @@ func update(x *vigo.X, req *UpdateRequest) (*models.OAuthProvider, error) {
// 如果提供了新的 ClientSecret则加密否则保留原值 // 如果提供了新的 ClientSecret则加密否则保留原值
if req.OAuthProvider.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 { if err != nil {
return nil, vigo.ErrInternalServer.WithString("failed to encrypt client secret") return nil, vigo.ErrInternalServer.WithString("failed to encrypt client secret")
} }

@ -50,8 +50,8 @@ type InitAdminConfig struct {
Email string `json:"email" usage:"管理员邮箱"` Email string `json:"email" usage:"管理员邮箱"`
} }
// Config 全局配置实例 // Global全局配置实例
var Config = &Options{ var Global = &Options{
DB: config.Database{ DB: config.Database{
Type: "mysql", Type: "mysql",
DSN: "root:123456@tcp(127.0.0.1:3306)/vbase?charset=utf8&parseTime=True&loc=Local", 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

@ -9,50 +9,8 @@ package main
import ( import (
"github.com/veypi/vbase" "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() { func main() {
cmdMain.Parse() panic(vbase.App.Run())
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()
} }

@ -12,6 +12,7 @@ import (
"github.com/veypi/vbase/api" "github.com/veypi/vbase/api"
"github.com/veypi/vbase/auth" "github.com/veypi/vbase/auth"
"github.com/veypi/vbase/cfg" "github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/models"
"github.com/veypi/vhtml" "github.com/veypi/vhtml"
vhtmlui "github.com/veypi/vhtml-ui" vhtmlui "github.com/veypi/vhtml-ui"
"github.com/veypi/vigo" "github.com/veypi/vigo"
@ -21,7 +22,6 @@ var Router = vigo.NewRouter()
var ( var (
Auth = auth.Factory Auth = auth.Factory
Config = cfg.Config
AuthMiddleware = auth.AuthMiddleware AuthMiddleware = auth.AuthMiddleware
) )
@ -34,3 +34,9 @@ func init() {
Router.Extend("vhtml", vhtml.Router) Router.Extend("vhtml", vhtml.Router)
vhtml.WrapUI(Router, uifs) vhtml.WrapUI(Router, uifs)
} }
var App = vigo.New("vbase", Router, cfg.Global, Init)
func Init() error {
return models.Migrate()
}

@ -23,14 +23,14 @@ var (
// Init 初始化Redis连接 // Init 初始化Redis连接
func Init() error { 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 return nil
} }
Client = redis.NewClient(&redis.Options{ Client = redis.NewClient(&redis.Options{
Addr: cfg.Config.Redis.Addr, Addr: cfg.Global.Redis.Addr,
Password: cfg.Config.Redis.Password, Password: cfg.Global.Redis.Password,
DB: cfg.Config.Redis.DB, DB: cfg.Global.Redis.DB,
}) })
if err := Client.Ping(Ctx).Err(); err != nil { if err := Client.Ping(Ctx).Err(); err != nil {
@ -42,7 +42,7 @@ func Init() error {
// IsEnabled 是否启用缓存 // IsEnabled 是否启用缓存
func IsEnabled() bool { 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 获取字符串值 // Get 获取字符串值

@ -68,7 +68,7 @@ func GenerateTokenPair(userID, username, nickname, avatar, email string, orgs []
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
TokenType: "Bearer", TokenType: "Bearer",
ExpiresIn: int(cfg.Config.JWT.AccessExpiry.Seconds()), ExpiresIn: int(cfg.Global.JWT.AccessExpiry.Seconds()),
}, nil }, nil
} }
@ -78,12 +78,12 @@ func GenerateAccessToken(userID, username, nickname, avatar, email string, orgs
claims := Claims{ claims := Claims{
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ID: uuid.New().String(), // jti ID: uuid.New().String(), // jti
Issuer: cfg.Config.JWT.Issuer, Issuer: cfg.Global.JWT.Issuer,
Subject: userID, Subject: userID,
Audience: jwt.ClaimStrings{"vbase"}, Audience: jwt.ClaimStrings{"vbase"},
IssuedAt: jwt.NewNumericDate(now), IssuedAt: jwt.NewNumericDate(now),
NotBefore: 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, UserID: userID,
Username: username, Username: username,
@ -95,7 +95,7 @@ func GenerateAccessToken(userID, username, nickname, avatar, email string, orgs
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(cfg.Config.JWT.Secret)) return token.SignedString([]byte(cfg.Global.JWT.Secret))
} }
// GenerateRefreshToken 生成刷新令牌 // GenerateRefreshToken 生成刷新令牌
@ -104,17 +104,17 @@ func GenerateRefreshToken(userID string) (string, error) {
claims := Claims{ claims := Claims{
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ID: uuid.New().String(), ID: uuid.New().String(),
Issuer: cfg.Config.JWT.Issuer, Issuer: cfg.Global.JWT.Issuer,
Subject: userID, Subject: userID,
IssuedAt: jwt.NewNumericDate(now), IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(cfg.Config.JWT.RefreshExpiry)), ExpiresAt: jwt.NewNumericDate(now.Add(cfg.Global.JWT.RefreshExpiry)),
}, },
UserID: userID, UserID: userID,
Type: "refresh", Type: "refresh",
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(cfg.Config.JWT.Secret)) return token.SignedString([]byte(cfg.Global.JWT.Secret))
} }
// ParseToken 解析Token // ParseToken 解析Token
@ -123,9 +123,8 @@ func ParseToken(tokenString string) (*Claims, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) 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 err != nil {
if errors.Is(err, jwt.ErrTokenExpired) { if errors.Is(err, jwt.ErrTokenExpired) {
return nil, ErrExpiredToken return nil, ErrExpiredToken

@ -72,7 +72,7 @@ func initAdminUser() error {
} }
// 检查 init_admin 配置 // 检查 init_admin 配置
adminCfg := cfg.Config.InitAdmin adminCfg := cfg.Global.InitAdmin
if adminCfg.Username == "" || adminCfg.Password == "" { if adminCfg.Username == "" || adminCfg.Password == "" {
// 未配置初始管理员,跳过 // 未配置初始管理员,跳过
return nil return nil

@ -44,12 +44,12 @@ func TestMain(m *testing.M) {
func setup() { func setup() {
// Configure for testing // Configure for testing
// Use in-memory SQLite database // Use in-memory SQLite database
cfg.Config.DB = config.Database{ cfg.Global.DB = config.Database{
Type: "sqlite", Type: "sqlite",
DSN: TestDBFile, DSN: TestDBFile,
} }
// Use mock/memory Redis // Use mock/memory Redis
cfg.Config.Redis = config.Redis{ cfg.Global.Redis = config.Redis{
Addr: "memory", Addr: "memory",
} }
// Initialize DB connection and run migrations // Initialize DB connection and run migrations

@ -11,7 +11,8 @@ export default async ($env) => {
} }
// Initialize VBase Service // Initialize VBase Service
const vbase = new VBase(''); // Relative path const vbase = new VBase('vb', '/'); // Relative path
console.log(vbase)
$env.$vbase = vbase; $env.$vbase = vbase;
// Wrap Axios // Wrap Axios

@ -41,7 +41,7 @@
<form @submit.prevent="handleLogin" style="display: grid; gap: 16px;"> <form @submit.prevent="handleLogin" style="display: grid; gap: 16px;">
<v-input label="Username" v:value="username" required placeholder="Enter username"></v-input> <v-input label="Username" v:value="username" required placeholder="Enter username"></v-input>
<v-input label="Password" type="password" v:value="password" required placeholder="Enter password"></v-input> <v-input label="Password" type="password" v:value="password" required placeholder="Enter password"></v-input>
<v-btn type="submit" color="primary" block style="margin-top: 8px;">{{ $t('auth.login') }}</v-btn> <v-btn type="submit" color="primary" block style="margin-top: 8px;">{{ $t('auth.login') }}</v-btn>
</form> </form>

@ -1,8 +1,14 @@
class VBase { class VBase {
constructor(baseURL, scope) { constructor(scope, baseURL) {
if (!baseURL) throw new Error('VBase: baseURL is required');
if (!scope) throw new Error('VBase: scope is required'); 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.baseURL = baseURL;
this.scope = scope; this.scope = scope;

Loading…
Cancel
Save