refactor: 重构服务器启动、路由加载的初始化

This commit is contained in:
Akizon77
2025-12-01 23:13:44 +08:00
parent f8b4e90d47
commit 801d9415ed
18 changed files with 166 additions and 100 deletions
+6 -2
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"log"
"os"
"github.com/gookit/event"
@@ -43,8 +44,11 @@ Made by Akizon77 with love.`,
}
func Execute() {
event.Trigger(eventType.ProcessStart, nil)
defer event.Trigger(eventType.ProcessExit, nil)
err, _ := event.Trigger(eventType.ProcessStart, event.M{})
if err != nil {
log.Fatalf("Something went wrong during process start: %v", err)
os.Exit(1)
}
if err := RootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
+20 -56
View File
@@ -7,7 +7,6 @@ import (
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
@@ -20,15 +19,11 @@ import (
"github.com/komari-monitor/komari/internal/database/auditlog"
"github.com/komari-monitor/komari/internal/database/dbcore"
"github.com/komari-monitor/komari/internal/database/models"
d_notification "github.com/komari-monitor/komari/internal/database/notification"
"github.com/komari-monitor/komari/internal/database/records"
"github.com/komari-monitor/komari/internal/database/tasks"
"github.com/komari-monitor/komari/internal/eventType"
logutil "github.com/komari-monitor/komari/internal/log"
"github.com/komari-monitor/komari/internal/messageSender"
"github.com/komari-monitor/komari/internal/patch"
"github.com/komari-monitor/komari/internal/restore"
"github.com/komari-monitor/komari/pkg/cloudflared"
"github.com/komari-monitor/komari/server"
"github.com/spf13/cobra"
)
@@ -55,54 +50,44 @@ func RunServer() {
if err := os.MkdirAll("./data/theme", os.ModePerm); err != nil {
log.Fatalf("Failed to create theme directory: %v", err)
}
// 进行备份恢复
if restore.NeedBackupRestore() {
restore.RestoreBackup()
}
conf.Load()
InitDatabase()
patch.ApplyPatch()
if conf.Version != conf.Version_Development {
gin.SetMode(gin.ReleaseMode)
}
config, err := conf.GetWithV1Format()
if err != nil {
log.Fatal(err)
}
r := gin.New()
r.Use(logutil.GinLogger())
r.Use(logutil.GinRecovery())
event.Trigger(eventType.ServerInitializeStart, event.M{"config": config, "engine": r})
InitDatabase()
patch.Apply()
err, _ := event.Trigger(eventType.ServerInitializeStart, event.M{"engine": r})
if err != nil {
log.Fatalf("Something went wrong during ServerInitializeStart event: %v", err)
os.Exit(1)
}
config, err := conf.GetWithV1Format()
if err != nil {
log.Fatal(err)
}
go DoScheduledWork()
go messageSender.Initialize()
server.StartNezhaGRPCServer(config.NezhaCompatListen)
// 初始化 cloudflared
if strings.ToLower(GetEnv("KOMARI_ENABLE_CLOUDFLARED", "false")) == "true" {
err := cloudflared.RunCloudflared() // 阻塞,确保cloudflared跑起来
if err != nil {
log.Fatalf("Failed to run cloudflared: %v", err)
}
}
server.Init(r)
srv := &http.Server{
Addr: flags.Listen,
Handler: r,
}
event.Trigger(eventType.ServerInitializeDone, event.M{"config": config})
log.Printf("Starting server on %s ...", flags.Listen)
event.Trigger(eventType.ServerInitializeDone, event.M{})
ScheduledEventTasksInit()
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
OnFatal(err)
event.Trigger(eventType.ProcessExit, event.M{})
log.Fatalf("listen: %s\n", err)
}
quit := make(chan os.Signal, 1)
@@ -119,18 +104,6 @@ func RunServer() {
}
func InitDatabase() {
// // 打印数据库类型和连接信息
// if flags.DatabaseType == "mysql" {
// log.Printf("使用 MySQL 数据库连接: %s@%s:%s/%s",
// flags.DatabaseUser, flags.DatabaseHost, flags.DatabasePort, flags.DatabaseName)
// log.Printf("环境变量配置: [KOMARI_DB_TYPE=%s] [KOMARI_DB_HOST=%s] [KOMARI_DB_PORT=%s] [KOMARI_DB_USER=%s] [KOMARI_DB_NAME=%s]",
// os.Getenv("KOMARI_DB_TYPE"), os.Getenv("KOMARI_DB_HOST"), os.Getenv("KOMARI_DB_PORT"),
// os.Getenv("KOMARI_DB_USER"), os.Getenv("KOMARI_DB_NAME"))
// } else {
// log.Printf("使用 SQLite 数据库文件: %s", flags.DatabaseFile)
// log.Printf("环境变量配置: [KOMARI_DB_TYPE=%s] [KOMARI_DB_FILE=%s]",
// os.Getenv("KOMARI_DB_TYPE"), os.Getenv("KOMARI_DB_FILE"))
// }
var count int64 = 0
if dbcore.GetDBInstance().Model(&models.User{}).Count(&count); count == 0 {
user, passwd, err := accounts.CreateDefaultAdminAccount()
@@ -144,12 +117,10 @@ func InitDatabase() {
// #region 定时任务
func DoScheduledWork() {
tasks.ReloadPingSchedule()
d_notification.ReloadLoadNotificationSchedule()
//records.DeleteRecordBefore(time.Now().Add(-time.Hour * 24 * 30))
records.CompactRecord()
ScheduledEventTasksInit()
event.On(eventType.SchedulerEvery30Minutes, event.ListenerFunc(func(e event.Event) error {
cfg, err := conf.GetWithV1Format()
@@ -183,12 +154,10 @@ func DoScheduledWork() {
func OnShutdown() {
auditlog.Log("", "", "server is shutting down", "info")
cloudflared.Kill()
}
func OnFatal(err error) {
auditlog.Log("", "", "server encountered a fatal error: "+err.Error(), "error")
cloudflared.Kill()
}
func ScheduledEventTasksInit() {
@@ -198,22 +167,17 @@ func ScheduledEventTasksInit() {
every1h := time.NewTicker(1 * time.Hour)
every1d := time.NewTicker(24 * time.Hour)
for {
var err error = nil
var e event.Event
select {
case <-every1m.C:
err, e = event.Trigger(eventType.SchedulerEveryMinute, event.M{"interval": "1m"})
event.Async(eventType.SchedulerEveryMinute, event.M{"interval": "1m"})
case <-every5m.C:
err, e = event.Trigger(eventType.SchedulerEvery5Minutes, event.M{"interval": "5m"})
event.Async(eventType.SchedulerEvery5Minutes, event.M{"interval": "5m"})
case <-every30m.C:
err, e = event.Trigger(eventType.SchedulerEvery30Minutes, event.M{"interval": "30m"})
event.Async(eventType.SchedulerEvery30Minutes, event.M{"interval": "30m"})
case <-every1h.C:
err, e = event.Trigger(eventType.SchedulerEveryHour, event.M{"interval": "1h"})
event.Async(eventType.SchedulerEveryHour, event.M{"interval": "1h"})
case <-every1d.C:
err, e = event.Trigger(eventType.SchedulerEveryDay, event.M{"interval": "1d"})
}
if err != nil {
slog.Warn("Scheduled task error:", "error", err, "event", e)
event.Async(eventType.SchedulerEveryDay, event.M{"interval": "1d"})
}
}
}
+1
View File
@@ -4,6 +4,7 @@ import (
// Import all internal packages to ensure their init() functions are executed
_ "github.com/komari-monitor/komari/internal/api_rpc"
_ "github.com/komari-monitor/komari/internal/client"
_ "github.com/komari-monitor/komari/internal/cloudflared"
_ "github.com/komari-monitor/komari/internal/common"
_ "github.com/komari-monitor/komari/internal/conf"
_ "github.com/komari-monitor/komari/internal/database"
@@ -5,7 +5,7 @@ import (
"testing"
"time"
"github.com/komari-monitor/komari/pkg/cloudflared"
"github.com/komari-monitor/komari/internal/cloudflared"
)
func TestRunCloudflared(t *testing.T) {
+28
View File
@@ -0,0 +1,28 @@
package cloudflared
import (
"fmt"
"os"
"strings"
"github.com/gookit/event"
"github.com/komari-monitor/komari/internal/eventType"
)
func init() {
event.On(eventType.ServerInitializeStart, event.ListenerFunc(func(e event.Event) error {
if strings.ToLower(strings.ToLower(os.Getenv("KOMARI_ENABLE_CLOUDFLARED"))) == "true" {
err := RunCloudflared()
if err != nil {
// Error in ServerInitializeStart will cause the process to exit
return fmt.Errorf("failed to run cloudflared: %v", err)
}
}
return nil
}))
event.On(eventType.ProcessExit, event.ListenerFunc(func(e event.Event) error {
Kill()
return nil
}))
}
-13
View File
@@ -97,19 +97,6 @@ func SaveFull(cst Config) error {
return Override(cst)
}
func Load() (*Config, error) {
b, err := os.ReadFile(flags.ConfigFile)
if err != nil {
return nil, err
}
cst := &Config{}
if err := json.Unmarshal(b, cst); err != nil {
return nil, err
}
Conf = cst
return cst, nil
}
// GetWithV1Format 以 v1 API 格式获取配置对象,使用 Conf 直接获取对象引用
func GetWithV1Format() (V1Struct, error) {
return Conf.ToV1Format(), nil
+35
View File
@@ -0,0 +1,35 @@
package conf
import (
"encoding/json"
"os"
"github.com/gookit/event"
"github.com/komari-monitor/komari/cmd/flags"
"github.com/komari-monitor/komari/internal/eventType"
)
func Init() {
b, err := os.ReadFile(flags.ConfigFile)
if err != nil {
panic(err)
}
cst := &Config{}
if err := json.Unmarshal(b, cst); err != nil {
panic(err)
}
Conf = cst
event.Trigger(eventType.ConfigUpdated, event.M{
"old": Config{},
"new": *Conf,
})
}
func init() {
Conf = &Config{}
event.On(eventType.ProcessStart, event.ListenerFunc(func(e event.Event) error {
Init()
return nil
}))
}
+9
View File
@@ -5,8 +5,10 @@ import (
"log"
"sync"
"github.com/gookit/event"
"github.com/komari-monitor/komari/cmd/flags"
"github.com/komari-monitor/komari/internal/database/models"
"github.com/komari-monitor/komari/internal/eventType"
logutil "github.com/komari-monitor/komari/internal/log"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
@@ -106,3 +108,10 @@ func GetDBInstance() *gorm.DB {
})
return instance
}
func init() {
event.On(eventType.SchedulerEvery5Minutes, event.ListenerFunc(func(e event.Event) error {
instance.Exec("PRAGMA wal_checkpoint(TRUNCATE);")
return nil
}))
}
+2 -6
View File
@@ -84,13 +84,9 @@ func DeleteAllPingRecords() error {
}
return result.Error
}
func ReloadPingSchedule() error {
db := dbcore.GetDBInstance()
var pingTasks []models.PingTask
if err := db.Find(&pingTasks).Error; err != nil {
return err
}
return pingSchedule.ReloadPingSchedule(pingTasks)
return pingSchedule.ReloadPingSchedule()
}
func GetPingRecords(uuid string, taskId int, start, end time.Time) ([]models.PingRecord, error) {
+4 -4
View File
@@ -21,12 +21,12 @@ const (
LoginFailed = "user.login.failed" // 用户登录失败
ConfigUpdated = "config.updated" // 配置更改
ConfigUpdated = "config.updated" // 配置更改。当服务器第一次启动载入配置时也会触发此事件,其中 old 全部为go类型零值, new 为载入的配置内容,若在配置载入前读取Conf,返回的将是零值配置。
ProcessStart = "process.start" // 进程启动
ProcessStart = "process.start" // 进程启动,如果在此事件的监听器中返回错误,进程将退出
ProcessExit = "process.exit" // 进程停止
ServerInitializeStart = "server.routers.start" // 服务器路由初始化开始
ServerInitializeStart = "server.routers.start" // 服务器路由初始化开始,如果在此事件的监听器中返回错误,进程将退出
ServerInitializeDone = "server.routers.done" // 服务器路由初始化完成
ServerListenGrpcStart = "server.listen.grpc.start" // 服务器开始监听 gRPC
ServerListenGrpcStop = "server.listen.grpc.stop" // 服务器停止监听 gRPC
@@ -41,7 +41,7 @@ const (
SchedulerEvery5Minutes = "scheduler.every5minutes" // 每五分钟定时触发
SchedulerEvery30Minutes = "scheduler.every30minutes" // 每三十分钟定时触发
SchedulerEveryHour = "scheduler.everyhour" // 每小时定时触发
SchedulerEveryDay = "scheduler.everyday" // 每天定时触发
SchedulerEveryDay = "scheduler.everyday" // 每天定时触发,服务器启动的当天不会触发此事件
NotificationSent = "notification.sent" // 通知发送
NotificationFailed = "notification.failed" // 通知发送失败
+1 -1
View File
@@ -25,7 +25,7 @@ func init() {
CurrentProvider = &EmptyProvider{}
geoCache = cache.New(48*time.Hour, 1*time.Hour)
err := SetProvider(conf.Conf.GeoIp.GeoIpProvider)
err := SetProvider("empty")
if err != nil {
log.Printf("Failed to set initial GeoIP provider: %v", err)
}
+5 -1
View File
@@ -6,9 +6,13 @@ import (
)
func init() {
go CheckExpireScheduledWork()
event.On(eventType.SchedulerEveryMinute, event.ListenerFunc(func(e event.Event) error {
CheckTraffic()
return nil
}))
event.On(eventType.ServerInitializeDone, event.ListenerFunc(func(e event.Event) error {
go CheckExpireScheduledWork()
ReloadLoadNotification()
return nil
}))
}
+13 -13
View File
@@ -71,19 +71,19 @@ func init() {
}
}
cfg, _ := conf.GetWithV1Format()
if cfg.OAuthProvider == "" || cfg.OAuthProvider == "none" {
LoadProvider("empty", "{}")
}
provider, err := database.GetOidcConfigByName(cfg.OAuthProvider)
if err != nil {
// 如果没有找到配置,使用empty provider
LoadProvider("empty", "{}")
}
err = LoadProvider(provider.Name, provider.Addition)
if err != nil {
log.Printf("Failed to load OIDC provider %s: %v", provider.Name, err)
}
// cfg, _ := conf.GetWithV1Format()
// if cfg.OAuthProvider == "" || cfg.OAuthProvider == "none" {
// LoadProvider("empty", "{}")
// }
// provider, err := database.GetOidcConfigByName(cfg.OAuthProvider)
// if err != nil {
// // 如果没有找到配置,使用empty provider
// LoadProvider("empty", "{}")
// }
// err = LoadProvider(provider.Name, provider.Addition)
// if err != nil {
// log.Printf("Failed to load OIDC provider %s: %v", provider.Name, err)
// }
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
oldConf, newConf, err := conf.FromEvent(e)
+2 -1
View File
@@ -8,7 +8,8 @@ import (
"github.com/komari-monitor/komari/internal/database/models"
)
func ApplyPatch() {
func Apply() {
db := dbcore.GetDBInstance()
// 0.0.5 迁移ClientInfo
if db.Migrator().HasTable("client_infos") {
+12
View File
@@ -0,0 +1,12 @@
package pingSchedule
import (
"github.com/gookit/event"
"github.com/komari-monitor/komari/internal/eventType"
)
func init() {
event.On(eventType.ServerInitializeDone, event.ListenerFunc(func(e event.Event) error {
return ReloadPingSchedule()
}))
}
+12 -2
View File
@@ -5,6 +5,7 @@ import (
"sync"
"time"
"github.com/komari-monitor/komari/internal/database/dbcore"
"github.com/komari-monitor/komari/internal/database/models"
"github.com/komari-monitor/komari/internal/ws"
)
@@ -107,7 +108,16 @@ func executePingTask(ctx context.Context, task models.PingTask, onlineClients ma
}
}
// ReloadPingSchedule 加载或重载时间表
func ReloadPingSchedule(pingTasks []models.PingTask) error {
// ReloadPingScheduleWithTasks 加载或重载时间表
func ReloadPingScheduleWithTasks(pingTasks []models.PingTask) error {
return manager.Reload(pingTasks)
}
func ReloadPingSchedule() error {
db := dbcore.GetDBInstance()
var pingTasks []models.PingTask
if err := db.Find(&pingTasks).Error; err != nil {
return err
}
return manager.Reload(pingTasks)
}
+15
View File
@@ -0,0 +1,15 @@
package restore
import (
"github.com/gookit/event"
"github.com/komari-monitor/komari/internal/eventType"
)
func init() {
event.On(eventType.ProcessStart, event.ListenerFunc(func(e event.Event) error {
if NeedBackupRestore() {
RestoreBackup()
}
return nil
}))
}