Skip to content
2 changes: 0 additions & 2 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ name: Docker
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

env:
REGISTRY: ghcr.io
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Build instructions
1. Go Build binary should be in `./build` folder. DON'T BUILD ELSEWHERE.
2. NPM has hot reload, no need to rerun to see changes in real-time

64 changes: 64 additions & 0 deletions internal/app/database/global.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package database

import (
"sync"

"github.com/GT-610/tairitsu/internal/app/logger"
"go.uber.org/zap"
)

var (
globalDB DBInterface
globalDBMu sync.RWMutex
)

// SetGlobalDB sets the global database instance
func SetGlobalDB(db DBInterface) {
globalDBMu.Lock()
defer globalDBMu.Unlock()
if globalDB != nil {
globalDB.Close()
logger.Info("已关闭旧的数据库连接")
}
globalDB = db
logger.Info("全局数据库实例已设置")
}

// GetGlobalDB returns the global database instance
func GetGlobalDB() DBInterface {
globalDBMu.RLock()
defer globalDBMu.RUnlock()
return globalDB
}

// InitGlobalDB initializes the global database from config
func InitGlobalDB() error {
globalDBMu.Lock()
defer globalDBMu.Unlock()

config := LoadConfig()
if config.Type == "" {
logger.Warn("数据库配置为空,跳过全局数据库初始化")
return nil
}

db, err := NewDatabase(config)
if err != nil {
logger.Error("创建数据库实例失败", zap.Error(err))
return err
}

if err := db.Init(); err != nil {
logger.Error("初始化数据库失败", zap.Error(err))
db.Close()
return err
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
}

if globalDB != nil {
globalDB.Close()
logger.Info("已关闭旧的数据库连接")
}
globalDB = db
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
logger.Info("全局数据库初始化成功", zap.String("type", string(config.Type)))
return nil
}
22 changes: 10 additions & 12 deletions internal/app/handlers/system_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ func (h *SystemHandler) ConfigureDatabase(c fiber.Ctx) error {

logger.Info("开始配置数据库", zap.String("type", dbConfig.Type))

// Validate database configuration
dbCfg := database.Config{
Type: database.DatabaseType(dbConfig.Type),
Path: dbConfig.Path,
Expand All @@ -116,40 +115,39 @@ func (h *SystemHandler) ConfigureDatabase(c fiber.Ctx) error {
Name: dbConfig.Name,
}

// Try connecting to database
db, err := database.NewDatabase(dbCfg)
if err != nil {
logger.Error("数据库连接失败", zap.Error(err))
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "数据库连接失败: " + err.Error()})
}

// Initialize database schema
if err := db.Init(); err != nil {
logger.Error("数据库初始化失败", zap.Error(err))
db.Close()
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "数据库初始化失败: " + err.Error()})
}

// Close database connection
if err := db.Close(); err != nil {
logger.Warn("关闭数据库连接时出现警告", zap.Error(err))
}

// For SQLite, ensure path is properly saved to config
// NewDatabase function might set default path if Path is empty
if dbCfg.Type == database.SQLite {
// From factory.go we know if Path is empty, default value "data/tairitsu.db" will be used
if dbConfig.Path == "" {
dbCfg.Path = "data/tairitsu.db"
}
logger.Info("SQLite数据库路径已设置", zap.String("path", dbCfg.Path))
}

// Save database configuration to unified config management module
if err := database.SaveConfig(dbCfg); err != nil {
logger.Error("保存数据库配置失败", zap.Error(err))
db.Close()
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "保存数据库配置失败: " + err.Error()})
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

if h.userService != nil {
h.userService.SetDB(db)
}
if h.networkService != nil {
h.networkService.SetDB(db)
}
database.SetGlobalDB(db)

logger.Info("数据库配置成功", zap.String("type", dbConfig.Type))
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "数据库配置成功",
Expand Down
31 changes: 21 additions & 10 deletions internal/app/initializer/server_initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,34 @@ func (si *ServerInitializer) setupRoutes() error {
return nil
}

// ReloadRoutes 重新加载所有API路由
// ReloadRoutes 更新数据库连接
func (si *ServerInitializer) ReloadRoutes() {
logger.Info("开始重新加载路由")
logger.Info("开始更新数据库连接")

si.routerMutex.Lock()
defer si.routerMutex.Unlock()

// 清除现有路由,创建新的路由器实例
si.router = fiber.New()

// 重新注册路由
if err := si.setupRoutes(); err != nil {
logger.Error("重新注册路由失败", zap.Error(err))
return
si.db = database.GetGlobalDB()
if si.db == nil {
dbConfig := database.LoadConfig()
if dbConfig.Type != "" {
var err error
si.db, err = database.NewDatabase(dbConfig)
if err != nil {
logger.Error("重新连接数据库失败", zap.Error(err))
return
}
if err := si.db.Init(); err != nil {
logger.Error("重新初始化数据库失败", zap.Error(err))
si.db.Close()
si.db = nil
return
}
database.SetGlobalDB(si.db)
Comment thread
GT-610 marked this conversation as resolved.
}
}
Comment thread
GT-610 marked this conversation as resolved.

logger.Info("路由重新加载完成")
logger.Info("数据库连接更新完成")
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.

// GetRouter 获取路由器实例
Expand Down
54 changes: 21 additions & 33 deletions internal/app/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,46 +108,34 @@ func SetupRoutesWithReload(router *fiber.App, ztClient *zerotier.Client, jwtSecr
// Reload routes (no authentication required, available during initial setup only)
api.Post("/system/reload", systemHandler.ReloadRoutes)

// Authentication routes (no authentication required)
auth := api.Group("/auth")
{
// Only enable registration and login if database is configured
if db != nil {
auth.Post("/register", authHandler.Register) // User registration
auth.Post("/login", authHandler.Login) // User login
}
auth.Post("/register", authHandler.Register)
auth.Post("/login", authHandler.Login)
}

// Authenticated routes
authenticated := api.Group("/")
authenticated.Use(authMiddleware)
{
// Only enable database-dependent features if database is configured
if db != nil {
// User information
authenticated.Get("/profile", authHandler.GetProfile) // Get current user info
authenticated.Post("/auth/update-password", authHandler.ChangePassword) // Update user password (deprecated)
authenticated.Put("/profile/password", authHandler.ChangePassword) // Update user password

// ZeroTier status
authenticated.Get("/status", networkHandler.GetStatus)

// Network management
networks := authenticated.Group("/networks")
{
networks.Get("", networkHandler.GetNetworks) // Get all networks
networks.Post("", networkHandler.CreateNetwork) // Create network
networks.Get("/:id", networkHandler.GetNetwork) // Get single network
networks.Put("/:id", networkHandler.UpdateNetwork) // Update network
networks.Put("/:id/metadata", networkHandler.UpdateNetworkMetadata) // Update network metadata (name and description)
networks.Delete("/:id", networkHandler.DeleteNetwork) // Delete network

// Member management (nested within network routes)
networks.Get("/:id/members", memberHandler.GetMembers) // Get member list
networks.Get("/:id/members/:memberId", memberHandler.GetMember) // Get single member
networks.Put("/:id/members/:memberId", memberHandler.UpdateMember) // Update member
networks.Delete("/:id/members/:memberId", memberHandler.DeleteMember) // Delete member
}
authenticated.Get("/profile", authHandler.GetProfile)
authenticated.Post("/auth/update-password", authHandler.ChangePassword)
authenticated.Put("/profile/password", authHandler.ChangePassword)

authenticated.Get("/status", networkHandler.GetStatus)

networks := authenticated.Group("/networks")
{
networks.Get("", networkHandler.GetNetworks)
networks.Post("", networkHandler.CreateNetwork)
networks.Get("/:id", networkHandler.GetNetwork)
networks.Put("/:id", networkHandler.UpdateNetwork)
networks.Put("/:id/metadata", networkHandler.UpdateNetworkMetadata)
networks.Delete("/:id", networkHandler.DeleteNetwork)

networks.Get("/:id/members", memberHandler.GetMembers)
networks.Get("/:id/members/:memberId", memberHandler.GetMember)
networks.Put("/:id/members/:memberId", memberHandler.UpdateMember)
networks.Delete("/:id/members/:memberId", memberHandler.DeleteMember)
}
}

Expand Down
Loading