mirror of
https://github.com/gazer-x/komari.git
synced 2026-06-22 00:05:52 +08:00
220 lines
6.6 KiB
Go
220 lines
6.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gookit/event"
|
|
"github.com/komari-monitor/komari/cmd/flags"
|
|
api "github.com/komari-monitor/komari/internal/api_v1"
|
|
"github.com/komari-monitor/komari/internal/conf"
|
|
"github.com/komari-monitor/komari/internal/database/accounts"
|
|
"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"
|
|
)
|
|
|
|
var ServerCmd = &cobra.Command{
|
|
Use: "server",
|
|
Short: "Start the server",
|
|
Long: `Start the server`,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
RunServer()
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
// 从环境变量获取监听地址
|
|
listenAddr := GetEnv("KOMARI_LISTEN", "0.0.0.0:25774")
|
|
ServerCmd.PersistentFlags().StringVarP(&flags.Listen, "listen", "l", listenAddr, "监听地址 [env: KOMARI_LISTEN]")
|
|
RootCmd.AddCommand(ServerCmd)
|
|
}
|
|
|
|
func RunServer() {
|
|
// #region 初始化
|
|
// 创建目录
|
|
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})
|
|
|
|
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)
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
OnFatal(err)
|
|
log.Fatalf("listen: %s\n", err)
|
|
}
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
|
|
<-quit
|
|
OnShutdown()
|
|
event.Trigger(eventType.ProcessExit, event.M{})
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
if err := srv.Shutdown(ctx); err != nil {
|
|
log.Fatalf("Server forced to shutdown: %v", err)
|
|
}
|
|
|
|
}
|
|
|
|
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()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
log.Println("Default admin account created. Username:", user, ", Password:", passwd)
|
|
}
|
|
}
|
|
|
|
// #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()
|
|
if err != nil {
|
|
slog.Warn("Failed to get config in scheduled task:", "error", err)
|
|
return err
|
|
}
|
|
records.DeleteRecordBefore(time.Now().Add(-time.Hour * time.Duration(cfg.RecordPreserveTime)))
|
|
records.CompactRecord()
|
|
tasks.ClearTaskResultsByTimeBefore(time.Now().Add(-time.Hour * time.Duration(cfg.RecordPreserveTime)))
|
|
tasks.DeletePingRecordsBefore(time.Now().Add(-time.Hour * time.Duration(cfg.PingRecordPreserveTime)))
|
|
auditlog.RemoveOldLogs()
|
|
return nil
|
|
}))
|
|
|
|
event.On(eventType.SchedulerEveryMinute, event.ListenerFunc(func(e event.Event) error {
|
|
cfg, err := conf.GetWithV1Format()
|
|
if err != nil {
|
|
slog.Warn("Failed to get config in scheduled task:", "error", err)
|
|
return err
|
|
}
|
|
api.SaveClientReportToDB()
|
|
if !cfg.RecordEnabled {
|
|
records.DeleteAll()
|
|
tasks.DeleteAllPingRecords()
|
|
}
|
|
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
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() {
|
|
every1m := time.NewTicker(1 * time.Minute)
|
|
every5m := time.NewTicker(5 * time.Minute)
|
|
every30m := time.NewTicker(30 * time.Minute)
|
|
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"})
|
|
case <-every5m.C:
|
|
err, e = event.Trigger(eventType.SchedulerEvery5Minutes, event.M{"interval": "5m"})
|
|
case <-every30m.C:
|
|
err, e = event.Trigger(eventType.SchedulerEvery30Minutes, event.M{"interval": "30m"})
|
|
case <-every1h.C:
|
|
err, e = event.Trigger(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)
|
|
}
|
|
}
|
|
}
|