mirror of
https://github.com/gazer-x/komari.git
synced 2026-06-21 15:55:51 +08:00
+7
-2
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"log/slog"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/cmd/flags"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
@@ -43,8 +45,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 {
|
||||
slog.Error("Something went wrong during process start.", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := RootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
|
||||
+77
-120
@@ -2,37 +2,24 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database"
|
||||
"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"
|
||||
"github.com/komari-monitor/komari/internal/geoip"
|
||||
logutil "github.com/komari-monitor/komari/internal/log"
|
||||
"github.com/komari-monitor/komari/internal/messageSender"
|
||||
"github.com/komari-monitor/komari/internal/notifier"
|
||||
"github.com/komari-monitor/komari/internal/oauth"
|
||||
"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"
|
||||
)
|
||||
@@ -59,63 +46,19 @@ 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()
|
||||
|
||||
internal.All()
|
||||
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 geoip.InitGeoIp()
|
||||
go DoScheduledWork()
|
||||
go messageSender.Initialize()
|
||||
go oauth.Initialize()
|
||||
|
||||
server.StartNezhaGRPCServer(config.NezhaCompatListen)
|
||||
|
||||
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
|
||||
newConf := e.Get("new").(conf.Config)
|
||||
oldConf := e.Get("old").(conf.Config)
|
||||
if newConf.Login.OAuthProvider != oldConf.Login.OAuthProvider {
|
||||
oidcProvider, err := database.GetOidcConfigByName(newConf.Login.OAuthProvider)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get OIDC provider config: %v", err)
|
||||
} else {
|
||||
log.Printf("Using %s as OIDC provider", oidcProvider.Name)
|
||||
}
|
||||
err = oauth.LoadProvider(oidcProvider.Name, oidcProvider.Addition)
|
||||
if err != nil {
|
||||
auditlog.EventLog("error", fmt.Sprintf("Failed to load OIDC provider: %v", err))
|
||||
}
|
||||
}
|
||||
if newConf.Notification.NotificationMethod != oldConf.Notification.NotificationMethod {
|
||||
messageSender.Initialize()
|
||||
}
|
||||
return nil
|
||||
}), event.Max)
|
||||
|
||||
// 初始化 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)
|
||||
}
|
||||
err, _ := event.Trigger(eventType.ServerInitializeStart, event.M{"engine": r})
|
||||
if err != nil {
|
||||
slog.Error("Something went wrong during ServerInitializeStart event.", slog.Any("error", err))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
server.Init(r)
|
||||
@@ -125,17 +68,25 @@ func RunServer() {
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
event.Trigger(eventType.ServerInitializeDone, event.M{"config": config})
|
||||
event.Trigger(eventType.ServerInitializeDone, event.M{})
|
||||
ScheduledEventTasksInit()
|
||||
|
||||
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)
|
||||
|
||||
log.Printf("Starting server on %s ...", flags.Listen)
|
||||
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
OnFatal(err)
|
||||
event.Trigger(eventType.ProcessExit, event.M{})
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-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 {
|
||||
@@ -144,66 +95,72 @@ 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()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Println("Default admin account created. Username:", user, ", Password:", passwd)
|
||||
}
|
||||
}
|
||||
|
||||
// #region 定时任务
|
||||
func DoScheduledWork() {
|
||||
tasks.ReloadPingSchedule()
|
||||
d_notification.ReloadLoadNotificationSchedule()
|
||||
ticker := time.NewTicker(time.Minute * 30)
|
||||
minute := time.NewTicker(60 * time.Second)
|
||||
//records.DeleteRecordBefore(time.Now().Add(-time.Hour * 24 * 30))
|
||||
records.CompactRecord()
|
||||
cfg, _ := conf.GetWithV1Format()
|
||||
go notifier.CheckExpireScheduledWork()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
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()
|
||||
case <-minute.C:
|
||||
api.SaveClientReportToDB()
|
||||
if !cfg.RecordEnabled {
|
||||
records.DeleteAll()
|
||||
tasks.DeleteAllPingRecords()
|
||||
}
|
||||
// 每分钟检查一次流量提醒
|
||||
go notifier.CheckTraffic()
|
||||
}
|
||||
}
|
||||
|
||||
//records.DeleteRecordBefore(time.Now().Add(-time.Hour * 24 * 30))
|
||||
|
||||
records.CompactRecord()
|
||||
|
||||
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
|
||||
}
|
||||
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() {
|
||||
go DoScheduledWork()
|
||||
go func() {
|
||||
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 {
|
||||
select {
|
||||
case <-every1m.C:
|
||||
event.Async(eventType.SchedulerEveryMinute, event.M{"interval": "1m"})
|
||||
case <-every5m.C:
|
||||
event.Async(eventType.SchedulerEvery5Minutes, event.M{"interval": "5m"})
|
||||
case <-every30m.C:
|
||||
event.Async(eventType.SchedulerEvery30Minutes, event.M{"interval": "30m"})
|
||||
case <-every1h.C:
|
||||
event.Async(eventType.SchedulerEveryHour, event.M{"interval": "1h"})
|
||||
case <-every1d.C:
|
||||
event.Async(eventType.SchedulerEveryDay, event.M{"interval": "1d"})
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package internal
|
||||
|
||||
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/api_v1"
|
||||
_ "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"
|
||||
_ "github.com/komari-monitor/komari/internal/eventType"
|
||||
_ "github.com/komari-monitor/komari/internal/geoip"
|
||||
_ "github.com/komari-monitor/komari/internal/log"
|
||||
_ "github.com/komari-monitor/komari/internal/messageSender"
|
||||
_ "github.com/komari-monitor/komari/internal/nezha"
|
||||
_ "github.com/komari-monitor/komari/internal/notifier"
|
||||
_ "github.com/komari-monitor/komari/internal/oauth"
|
||||
_ "github.com/komari-monitor/komari/internal/patch"
|
||||
_ "github.com/komari-monitor/komari/internal/pingSchedule"
|
||||
_ "github.com/komari-monitor/komari/internal/plugin"
|
||||
_ "github.com/komari-monitor/komari/internal/renewal"
|
||||
_ "github.com/komari-monitor/komari/internal/restore"
|
||||
_ "github.com/komari-monitor/komari/internal/ws"
|
||||
)
|
||||
|
||||
func All() {}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/vars"
|
||||
"github.com/komari-monitor/komari/internal/common"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database"
|
||||
@@ -489,7 +489,7 @@ func getNodeRecentStatus(ctx context.Context, req *rpc.JsonRpcRequest) (any, *rp
|
||||
}
|
||||
}
|
||||
|
||||
raw, _ := api.Records.Get(params.UUID)
|
||||
raw, _ := vars.Records.Get(params.UUID)
|
||||
reports, _ := raw.([]common.Report)
|
||||
|
||||
// 扁平化为 { count, records: [] }
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package api_rpc
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
func init() {
|
||||
event.On(eventType.ServerInitializeStart, event.ListenerFunc(func(e event.Event) error {
|
||||
r := e.Get("engine").(*gin.Engine)
|
||||
RegisterRouters("/api/rpc2", r)
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package api_v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -20,7 +21,7 @@ func AdminAuthMiddleware() gin.HandlerFunc {
|
||||
// session-based authentication
|
||||
session, err := c.Cookie("session_token")
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusUnauthorized, "Unauthorized.")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "Unauthorized.")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
@@ -28,7 +29,7 @@ func AdminAuthMiddleware() gin.HandlerFunc {
|
||||
// Komari is a single user system
|
||||
uuid, err := accounts.GetSession(session)
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusUnauthorized, "Unauthorized.")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "Unauthorized.")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,45 +2,27 @@ package api_v1
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/vars"
|
||||
"github.com/komari-monitor/komari/internal/common"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/patrickmn/go-cache"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/pkg/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
Records = cache.New(1*time.Minute, 1*time.Minute)
|
||||
)
|
||||
|
||||
type TerminalSession struct {
|
||||
UUID string
|
||||
UserUUID string
|
||||
Browser *websocket.Conn
|
||||
Agent *websocket.Conn
|
||||
RequesterIp string
|
||||
}
|
||||
|
||||
var TerminalSessionsMutex = &sync.Mutex{}
|
||||
var TerminalSessions = make(map[string]*TerminalSession)
|
||||
|
||||
func SaveClientReportToDB() error {
|
||||
lastMinute := time.Now().Add(-time.Minute).Unix()
|
||||
var records []models.Record
|
||||
var gpuRecords []models.GPURecord
|
||||
|
||||
// 遍历所有客户端记录
|
||||
for uuid, x := range Records.Items() {
|
||||
for uuid, x := range vars.Records.Items() {
|
||||
if uuid == "" {
|
||||
continue
|
||||
}
|
||||
@@ -60,7 +42,7 @@ func SaveClientReportToDB() error {
|
||||
}
|
||||
|
||||
// 更新缓存
|
||||
Records.Set(uuid, filtered, cache.DefaultExpiration)
|
||||
vars.Records.Set(uuid, filtered, cache.DefaultExpiration)
|
||||
|
||||
// 计算平均报告并添加到记录列表
|
||||
if len(filtered) > 0 {
|
||||
@@ -113,38 +95,6 @@ func SaveClientReportToDB() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// Respond sends a standardized JSON response.
|
||||
func Respond(c *gin.Context, httpStatus int, status string, message string, data interface{}) {
|
||||
c.JSON(httpStatus, Response{Status: status, Message: message, Data: data})
|
||||
}
|
||||
|
||||
// RespondSuccess sends a success response with data.
|
||||
func RespondSuccess(c *gin.Context, data interface{}) {
|
||||
Respond(c, http.StatusOK, "success", "", data)
|
||||
}
|
||||
|
||||
// RespondSuccessMessage sends a success response with message and data.
|
||||
func RespondSuccessMessage(c *gin.Context, message string, data interface{}) {
|
||||
Respond(c, http.StatusOK, "success", message, data)
|
||||
}
|
||||
|
||||
// RespondError sends an error response with message.
|
||||
func RespondError(c *gin.Context, httpStatus int, message string) {
|
||||
Respond(c, httpStatus, "error", message, nil)
|
||||
}
|
||||
func GetVersion(c *gin.Context) {
|
||||
RespondSuccess(c, gin.H{
|
||||
"version": conf.Version,
|
||||
"hash": conf.CommitHash,
|
||||
})
|
||||
}
|
||||
|
||||
func isApiKeyValid(apiKey string) bool {
|
||||
cfg, err := conf.GetWithV1Format()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package api_v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
|
||||
@@ -48,7 +49,7 @@ func PrivateSiteMiddleware() gin.HandlerFunc {
|
||||
}
|
||||
conf, err := conf.GetWithV1Format()
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusInternalServerError, "Failed to get configuration.")
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to get configuration.")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
@@ -60,13 +61,13 @@ func PrivateSiteMiddleware() gin.HandlerFunc {
|
||||
// 如果是私有站点,检查是否有 session
|
||||
session, err := c.Cookie("session_token")
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusUnauthorized, "Private site is enabled, please login first.")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "Private site is enabled, please login first.")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
_, err = accounts.GetSession(session)
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusUnauthorized, "Private site is enabled, please login first.")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "Private site is enabled, please login first.")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"image/png"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func Generate2FA(c *gin.Context) {
|
||||
secret, img, err := accounts.Generate2Fa()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to generate 2FA: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to generate 2FA: "+err.Error())
|
||||
return
|
||||
}
|
||||
c.SetCookie("2fa_secret", secret, 1800, "/", "", false, true)
|
||||
@@ -26,29 +26,29 @@ func Enable2FA(c *gin.Context) {
|
||||
secret, _ := c.Cookie("2fa_secret")
|
||||
code := c.Query("code")
|
||||
if secret == "" || uuid == nil || code == "" {
|
||||
api.RespondError(c, 400, "2FA secret or code not provided")
|
||||
resp.RespondError(c, 400, "2FA secret or code not provided")
|
||||
return
|
||||
}
|
||||
if !totp.Validate(code, secret) {
|
||||
api.RespondError(c, 400, "Invalid 2FA code")
|
||||
resp.RespondError(c, 400, "Invalid 2FA code")
|
||||
return
|
||||
}
|
||||
err := accounts.Enable2Fa(uuid.(string), secret)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to enable 2FA: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to enable 2FA: "+err.Error())
|
||||
return
|
||||
}
|
||||
c.SetCookie("2fa_secret", "", -1, "/", "", false, true)
|
||||
|
||||
api.RespondSuccess(c, "2FA enabled successfully")
|
||||
resp.RespondSuccess(c, "2FA enabled successfully")
|
||||
}
|
||||
|
||||
func Disable2FA(c *gin.Context) {
|
||||
uuid, _ := c.Get("uuid")
|
||||
err := accounts.Disable2Fa(uuid.(string))
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to disable 2FA: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to disable 2FA: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, "")
|
||||
resp.RespondSuccess(c, "")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -11,18 +11,18 @@ import (
|
||||
func OrderWeight(c *gin.Context) {
|
||||
var req = make(map[string]int)
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
db := dbcore.GetDBInstance()
|
||||
for uuid, weight := range req {
|
||||
err := db.Model(&models.Client{}).Where("uuid = ?", uuid).Update("weight", weight).Error
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to update client weight: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to update client weight: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "order clients", "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
clipboardDB "github.com/komari-monitor/komari/internal/database/clipboard"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -16,41 +16,41 @@ func GetClipboard(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid ID")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
cb, err := clipboardDB.GetClipboardByID(id)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to get clipboard: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to get clipboard: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, cb)
|
||||
resp.RespondSuccess(c, cb)
|
||||
}
|
||||
|
||||
// ListClipboard lists all clipboard entries
|
||||
func ListClipboard(c *gin.Context) {
|
||||
list, err := clipboardDB.ListClipboard()
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to list clipboard: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to list clipboard: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, list)
|
||||
resp.RespondSuccess(c, list)
|
||||
}
|
||||
|
||||
// CreateClipboard creates a new clipboard entry
|
||||
func CreateClipboard(c *gin.Context) {
|
||||
var req models.Clipboard
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
if err := clipboardDB.CreateClipboard(&req); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to create clipboard: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to create clipboard: "+err.Error())
|
||||
return
|
||||
}
|
||||
userUUID, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), userUUID.(string), "create clipboard:"+strconv.Itoa(req.Id), "info")
|
||||
api.RespondSuccess(c, req)
|
||||
resp.RespondSuccess(c, req)
|
||||
}
|
||||
|
||||
// UpdateClipboard updates an existing clipboard entry
|
||||
@@ -58,21 +58,21 @@ func UpdateClipboard(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid ID")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
var fields map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&fields); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
if err := clipboardDB.UpdateClipboardFields(id, fields); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to update clipboard: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to update clipboard: "+err.Error())
|
||||
return
|
||||
}
|
||||
userUUID, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), userUUID.(string), "update clipboard:"+strconv.Itoa(id), "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
// DeleteClipboard deletes a clipboard entry
|
||||
@@ -80,16 +80,16 @@ func DeleteClipboard(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid ID")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
if err := clipboardDB.DeleteClipboard(id); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to delete clipboard: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to delete clipboard: "+err.Error())
|
||||
return
|
||||
}
|
||||
userUUID, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), userUUID.(string), "delete clipboard:"+strconv.Itoa(id), "warn")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
// BatchDeleteClipboard deletes multiple clipboard entries
|
||||
@@ -98,18 +98,18 @@ func BatchDeleteClipboard(c *gin.Context) {
|
||||
IDs []int `json:"ids" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
if len(req.IDs) == 0 {
|
||||
api.RespondError(c, http.StatusBadRequest, "IDs cannot be empty")
|
||||
resp.RespondError(c, http.StatusBadRequest, "IDs cannot be empty")
|
||||
return
|
||||
}
|
||||
if err := clipboardDB.DeleteClipboardBatch(req.IDs); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to batch delete clipboard: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to batch delete clipboard: "+err.Error())
|
||||
return
|
||||
}
|
||||
userUUID, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), userUUID.(string), "batch delete clipboard: "+strconv.Itoa(len(req.IDs))+" items", "warn")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/cmd/flags"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
)
|
||||
|
||||
@@ -108,14 +108,14 @@ func DownloadBackup(c *gin.Context) {
|
||||
// 1) 创建临时目录
|
||||
tempDir, err := os.MkdirTemp("", "komari-backup-*")
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating temporary directory: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating temporary directory: %v", err))
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// 2) 复制 ./data 下除 .db/.db-wal/.db-shm 外的所有文件到临时目录
|
||||
if err := copyDataToTempExcludingDB(tempDir); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error copying data to temp: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error copying data to temp: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,18 +125,18 @@ func DownloadBackup(c *gin.Context) {
|
||||
|
||||
if flags.DatabaseType == "sqlite" || flags.DatabaseType == "" {
|
||||
if err := backupSQLiteTo(destDB); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error backing up sqlite database: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error backing up sqlite database: %v", err))
|
||||
return
|
||||
}
|
||||
} else if dbFilePath != "" {
|
||||
// 非 sqlite 的情况:若配置了文件路径且存在,则直接复制(按用户需求仍然将名称固定为 komari.db)
|
||||
if _, err := os.Stat(dbFilePath); err == nil {
|
||||
if err := copyFile(dbFilePath, destDB); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error copying database file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error copying database file: %v", err))
|
||||
return
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error stating database file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error stating database file: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func DownloadBackup(c *gin.Context) {
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error archiving temp folder: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error archiving temp folder: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -200,11 +200,11 @@ func DownloadBackup(c *gin.Context) {
|
||||
Modified: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating backup markup file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating backup markup file: %v", err))
|
||||
return
|
||||
}
|
||||
if _, err = markupWriter.Write([]byte(markupContent)); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error writing backup markup file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error writing backup markup file: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/database/tasks"
|
||||
@@ -25,7 +25,7 @@ func Exec(c *gin.Context) {
|
||||
var onlineClients []string
|
||||
var offlineClients []string
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
// for uuid := range ws.GetConnectedClients() {
|
||||
@@ -33,7 +33,7 @@ func Exec(c *gin.Context) {
|
||||
// onlineClients = append(onlineClients, uuid)
|
||||
// }
|
||||
// // else {
|
||||
// // api.RespondError(c, 400, "Client not connected: "+uuid)
|
||||
// // resp.RespondError(c, 400, "Client not connected: "+uuid)
|
||||
// // return
|
||||
// // }
|
||||
// }
|
||||
@@ -45,12 +45,12 @@ func Exec(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
if len(onlineClients) == 0 {
|
||||
api.RespondError(c, 400, "No clients connected")
|
||||
resp.RespondError(c, 400, "No clients connected")
|
||||
return
|
||||
}
|
||||
taskId := utils.GenerateRandomString(16)
|
||||
if err := tasks.CreateTask(taskId, append(onlineClients, offlineClients...), req.Command); err != nil {
|
||||
api.RespondError(c, 500, "Failed to create task: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to create task: "+err.Error())
|
||||
return
|
||||
}
|
||||
for _, uuid := range onlineClients {
|
||||
@@ -67,17 +67,17 @@ func Exec(c *gin.Context) {
|
||||
client := ws.GetConnectedClients()[uuid]
|
||||
if client != nil {
|
||||
if err := client.WriteMessage(websocket.TextMessage, payload); err != nil {
|
||||
api.RespondError(c, 400, "Client connection is broke: "+uuid)
|
||||
resp.RespondError(c, 400, "Client connection is broke: "+uuid)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
api.RespondError(c, 400, "Client connection is null: "+uuid)
|
||||
resp.RespondError(c, 400, "Client connection is null: "+uuid)
|
||||
return
|
||||
}
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "REC, task id: "+taskId, "warn")
|
||||
api.RespondSuccess(c, gin.H{
|
||||
resp.RespondSuccess(c, gin.H{
|
||||
"task_id": taskId,
|
||||
"clients": onlineClients,
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
)
|
||||
@@ -22,12 +22,12 @@ func GetLogs(c *gin.Context) {
|
||||
// If conversion fails, return an error
|
||||
limitInt, err := strconv.Atoi(limit)
|
||||
if err != nil || limitInt <= 0 {
|
||||
api.RespondError(c, 400, "Invalid limit: "+limit)
|
||||
resp.RespondError(c, 400, "Invalid limit: "+limit)
|
||||
return
|
||||
}
|
||||
pageInt, err := strconv.Atoi(page)
|
||||
if err != nil || pageInt <= 0 {
|
||||
api.RespondError(c, 400, "Invalid page: "+page)
|
||||
resp.RespondError(c, 400, "Invalid page: "+page)
|
||||
return
|
||||
}
|
||||
db := dbcore.GetDBInstance()
|
||||
@@ -37,13 +37,13 @@ func GetLogs(c *gin.Context) {
|
||||
|
||||
var total int64
|
||||
if err := db.Model(&models.Log{}).Count(&total).Error; err != nil {
|
||||
api.RespondError(c, 500, "Failed to count logs: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to count logs: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := db.Order("time desc").Limit(limitInt).Offset(offset).Find(&logs).Error; err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve logs: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve logs: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, gin.H{"logs": logs, "total": total})
|
||||
resp.RespondSuccess(c, gin.H{"logs": logs, "total": total})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -16,38 +16,38 @@ func GetMessageSenderProvider(c *gin.Context) {
|
||||
// 如果指定了provider,返回单个提供者的配置
|
||||
config, err := database.GetMessageSenderConfigByName(provider)
|
||||
if err != nil {
|
||||
api.RespondError(c, 404, "Provider not found: "+err.Error())
|
||||
resp.RespondError(c, 404, "Provider not found: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, config)
|
||||
resp.RespondSuccess(c, config)
|
||||
return
|
||||
}
|
||||
// 否则返回所有提供者的配置项模板
|
||||
providers := factory.GetSenderConfigs()
|
||||
if len(providers) == 0 {
|
||||
api.RespondError(c, 404, "No message sender providers found")
|
||||
resp.RespondError(c, 404, "No message sender providers found")
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, providers)
|
||||
resp.RespondSuccess(c, providers)
|
||||
}
|
||||
|
||||
func SetMessageSenderProvider(c *gin.Context) {
|
||||
var senderConfig models.MessageSenderProvider
|
||||
if err := c.ShouldBindJSON(&senderConfig); err != nil {
|
||||
api.RespondError(c, 400, "Invalid configuration: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid configuration: "+err.Error())
|
||||
return
|
||||
}
|
||||
if senderConfig.Name == "" {
|
||||
api.RespondError(c, 400, "Provider name is required")
|
||||
resp.RespondError(c, 400, "Provider name is required")
|
||||
return
|
||||
}
|
||||
_, exists := factory.GetConstructor(senderConfig.Name)
|
||||
if !exists {
|
||||
api.RespondError(c, 404, "Provider not found: "+senderConfig.Name)
|
||||
resp.RespondError(c, 404, "Provider not found: "+senderConfig.Name)
|
||||
return
|
||||
}
|
||||
if err := database.SaveMessageSenderConfig(&senderConfig); err != nil {
|
||||
api.RespondError(c, 500, "Failed to save message sender provider configuration: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to save message sender provider configuration: "+err.Error())
|
||||
return
|
||||
}
|
||||
cfg, _ := conf.GetWithV1Format()
|
||||
@@ -55,9 +55,9 @@ func SetMessageSenderProvider(c *gin.Context) {
|
||||
if cfg.NotificationMethod == senderConfig.Name {
|
||||
err := messageSender.LoadProvider(senderConfig.Name, senderConfig.Addition)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to load message sender provider: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to load message sender provider: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
api.RespondSuccess(c, gin.H{"message": "Message sender provider set successfully"})
|
||||
resp.RespondSuccess(c, gin.H{"message": "Message sender provider set successfully"})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/database/notification"
|
||||
)
|
||||
@@ -21,23 +21,23 @@ func AddLoadNotification(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if req.Interval > 4*60 || req.Interval <= 0 {
|
||||
api.RespondError(c, http.StatusBadRequest, "Interval must be between 1 and 240 minutes")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Interval must be between 1 and 240 minutes")
|
||||
return
|
||||
}
|
||||
if req.Ratio <= 0 || req.Ratio > 1 {
|
||||
api.RespondError(c, http.StatusBadRequest, "Ratio must be between 0 and 1")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Ratio must be between 0 and 1")
|
||||
return
|
||||
}
|
||||
|
||||
if taskID, err := notification.AddLoadNotification(req.Clients, req.Name, req.Metric, req.Threshold, req.Ratio, req.Interval); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
api.RespondSuccess(c, gin.H{"task_id": taskID})
|
||||
resp.RespondSuccess(c, gin.H{"task_id": taskID})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +47,14 @@ func DeleteLoadNotification(c *gin.Context) {
|
||||
ID []uint `json:"id" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := notification.DeleteLoadNotification(req.ID); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,26 +65,26 @@ func EditLoadNotification(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid request data")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request data")
|
||||
return
|
||||
}
|
||||
|
||||
if err := notification.EditLoadNotification(req.Notifications); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
// for _, notification := range req.Notifications {
|
||||
// notification.DeleteLoadNotification([]uint{notification.Id})
|
||||
// }
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func GetAllLoadNotifications(c *gin.Context) {
|
||||
notifications, err := notification.GetAllLoadNotifications()
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.RespondSuccess(c, notifications)
|
||||
resp.RespondSuccess(c, notifications)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package notification
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
resp "github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"gorm.io/gorm/clause"
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func EnableOfflineNotification(c *gin.Context) {
|
||||
var uuids []string
|
||||
if err := c.ShouldBindJSON(&uuids); err != nil {
|
||||
api.RespondError(c, 400, "Invalid request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
var notifications []models.OfflineNotification
|
||||
@@ -30,17 +30,17 @@ func EnableOfflineNotification(c *gin.Context) {
|
||||
Select("client", "enable").
|
||||
Create(notifications).Error
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to enable offline notifications: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to enable offline notifications: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
// POST body : []uuid
|
||||
func DisableOfflineNotification(c *gin.Context) {
|
||||
var uuids []string
|
||||
if err := c.ShouldBindJSON(&uuids); err != nil {
|
||||
api.RespondError(c, 400, "Invalid request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
var notifications []models.OfflineNotification
|
||||
@@ -58,29 +58,29 @@ func DisableOfflineNotification(c *gin.Context) {
|
||||
Select("client", "enable").
|
||||
Create(notifications).Error
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to disable offline notifications: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to disable offline notifications: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func EditOfflineNotification(c *gin.Context) {
|
||||
var notifications []models.OfflineNotification
|
||||
if err := c.ShouldBindJSON(¬ifications); err != nil {
|
||||
api.RespondError(c, 400, "Invalid request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
if len(notifications) == 0 {
|
||||
api.RespondError(c, 400, "At least one notification is required")
|
||||
resp.RespondError(c, 400, "At least one notification is required")
|
||||
return
|
||||
}
|
||||
for _, noti := range notifications {
|
||||
if noti.Client == "" {
|
||||
api.RespondError(c, 400, "Client UUID cannot be empty")
|
||||
resp.RespondError(c, 400, "Client UUID cannot be empty")
|
||||
return
|
||||
}
|
||||
if noti.GracePeriod <= 0 {
|
||||
api.RespondError(c, 400, "GracePeriod must be a positive integer")
|
||||
resp.RespondError(c, 400, "GracePeriod must be a positive integer")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -92,18 +92,18 @@ func EditOfflineNotification(c *gin.Context) {
|
||||
Select("*").
|
||||
Create(notifications).Error
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to edit offline notifications: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to edit offline notifications: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func ListOfflineNotifications(c *gin.Context) {
|
||||
var notifications []models.OfflineNotification
|
||||
err := dbcore.GetDBInstance().Model(&models.OfflineNotification{}).Find(¬ifications).Error
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to list offline notifications: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to list offline notifications: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, notifications)
|
||||
resp.RespondSuccess(c, notifications)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
@@ -15,7 +15,7 @@ func BindingExternalAccount(c *gin.Context) {
|
||||
session, _ := c.Cookie("session_token")
|
||||
user, err := accounts.GetUserBySession(session)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "No user found: "+err.Error())
|
||||
resp.RespondError(c, 500, "No user found: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,17 +26,17 @@ func UnbindExternalAccount(c *gin.Context) {
|
||||
session, _ := c.Cookie("session_token")
|
||||
user, err := accounts.GetUserBySession(session)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "No user found: "+err.Error())
|
||||
resp.RespondError(c, 500, "No user found: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = accounts.UnbindExternalAccount(user.UUID)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to unbind external account: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to unbind external account: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func GetOidcProvider(c *gin.Context) {
|
||||
@@ -45,39 +45,39 @@ func GetOidcProvider(c *gin.Context) {
|
||||
// 如果指定了provider,返回单个提供者的配置
|
||||
config, err := database.GetOidcConfigByName(provider)
|
||||
if err != nil {
|
||||
api.RespondError(c, 404, "Provider not found: "+err.Error())
|
||||
resp.RespondError(c, 404, "Provider not found: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, config)
|
||||
resp.RespondSuccess(c, config)
|
||||
return
|
||||
}
|
||||
// 否则返回所有提供者的配置
|
||||
providers := factory.GetProviderConfigs()
|
||||
if len(providers) == 0 {
|
||||
api.RespondError(c, 404, "No OIDC providers found")
|
||||
resp.RespondError(c, 404, "No OIDC providers found")
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, providers)
|
||||
resp.RespondSuccess(c, providers)
|
||||
}
|
||||
|
||||
func SetOidcProvider(c *gin.Context) {
|
||||
var oidcConfig models.OidcProvider
|
||||
if err := c.ShouldBindJSON(&oidcConfig); err != nil {
|
||||
api.RespondError(c, 400, "Invalid configuration: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid configuration: "+err.Error())
|
||||
return
|
||||
}
|
||||
if oidcConfig.Name == "" {
|
||||
api.RespondError(c, 400, "Provider name is required")
|
||||
resp.RespondError(c, 400, "Provider name is required")
|
||||
return
|
||||
}
|
||||
_, exists := factory.GetConstructor(oidcConfig.Name)
|
||||
if !exists {
|
||||
api.RespondError(c, 404, "Provider not found: "+oidcConfig.Name)
|
||||
resp.RespondError(c, 404, "Provider not found: "+oidcConfig.Name)
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.SaveOidcConfig(&oidcConfig); err != nil {
|
||||
api.RespondError(c, 500, "Failed to save OIDC provider configuration: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to save OIDC provider configuration: "+err.Error())
|
||||
return
|
||||
}
|
||||
cfg, _ := conf.GetWithV1Format()
|
||||
@@ -85,9 +85,9 @@ func SetOidcProvider(c *gin.Context) {
|
||||
if cfg.OAuthProvider == oidcConfig.Name {
|
||||
err := oauth.LoadProvider(oidcConfig.Name, oidcConfig.Addition)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to load OIDC provider: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to load OIDC provider: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
api.RespondSuccess(c, gin.H{"message": "OIDC provider set successfully"})
|
||||
resp.RespondSuccess(c, gin.H{"message": "OIDC provider set successfully"})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/database/tasks"
|
||||
)
|
||||
@@ -20,14 +20,14 @@ func AddPingTask(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if taskID, err := tasks.AddPingTask(req.Clients, req.Name, req.Target, req.TaskType, req.Interval); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
api.RespondSuccess(c, gin.H{"task_id": taskID})
|
||||
resp.RespondSuccess(c, gin.H{"task_id": taskID})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,14 +37,14 @@ func DeletePingTask(c *gin.Context) {
|
||||
ID []uint `json:"id" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := tasks.DeletePingTask(req.ID); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,26 +55,26 @@ func EditPingTask(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid request data")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request data")
|
||||
return
|
||||
}
|
||||
|
||||
if err := tasks.EditPingTask(req.Tasks); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
// for _, task := range req.Tasks {
|
||||
// tasks.DeletePingRecords([]uint{task.Id})
|
||||
// }
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func GetAllPingTasks(c *gin.Context) {
|
||||
tasks, err := tasks.GetAllPingTasks()
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.RespondSuccess(c, tasks)
|
||||
resp.RespondSuccess(c, tasks)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
|
||||
@@ -12,7 +12,7 @@ func GetSessions(c *gin.Context) {
|
||||
|
||||
ss, err := accounts.GetAllSessions()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve sessions: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve sessions: "+err.Error())
|
||||
return
|
||||
}
|
||||
current, _ := c.Cookie("session_token")
|
||||
@@ -24,27 +24,27 @@ func DeleteSession(c *gin.Context) {
|
||||
Session string `json:"session" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, 400, "Invalid request: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid request: "+err.Error())
|
||||
return
|
||||
}
|
||||
err := accounts.DeleteSession(req.Session)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to delete session: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to delete session: "+err.Error())
|
||||
return
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "delete session", "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func DeleteAllSession(c *gin.Context) {
|
||||
|
||||
err := accounts.DeleteAllSessions()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to delete all sessions: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to delete all sessions: "+err.Error())
|
||||
return
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "delete all sessions", "warn")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package admin
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/database/records"
|
||||
@@ -21,7 +21,7 @@ func GetSettings(c *gin.Context) {
|
||||
cst = conf.V1Struct{Sitename: "Komari"}
|
||||
cst.ID = 1
|
||||
conf.Save(cst)
|
||||
api.RespondSuccess(c, cst)
|
||||
resp.RespondSuccess(c, cst)
|
||||
return
|
||||
}
|
||||
c.JSON(500, gin.H{
|
||||
@@ -29,20 +29,20 @@ func GetSettings(c *gin.Context) {
|
||||
"message": "Internal Server Error: " + err.Error(),
|
||||
})
|
||||
}
|
||||
api.RespondSuccess(c, cst)
|
||||
resp.RespondSuccess(c, cst)
|
||||
}
|
||||
|
||||
// EditSettings 更新自定义配置
|
||||
func EditSettings(c *gin.Context) {
|
||||
cfg := make(map[string]interface{})
|
||||
if err := c.ShouldBindJSON(&cfg); err != nil {
|
||||
api.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cfg["id"] = 1 // Only one record
|
||||
if err := conf.Update(cfg); err != nil {
|
||||
api.RespondError(c, 500, "Failed to update settings: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to update settings: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func EditSettings(c *gin.Context) {
|
||||
message = message[:len(message)-2]
|
||||
}
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), message, "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func contains(slice []string, item string) bool {
|
||||
@@ -76,5 +76,5 @@ func ClearAllRecords(c *gin.Context) {
|
||||
tasks.DeleteAllPingRecords()
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "clear all records", "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -2,21 +2,21 @@ package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/tasks"
|
||||
)
|
||||
|
||||
func GetTasks(c *gin.Context) {
|
||||
dbTasks, err := tasks.GetAllTasks()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve tasks: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve tasks: "+err.Error())
|
||||
return
|
||||
}
|
||||
var responseTasks []gin.H
|
||||
for _, t := range dbTasks {
|
||||
results, err := tasks.GetTaskResultsByTaskId(t.TaskId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve task results: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve task results: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -38,27 +38,27 @@ func GetTasks(c *gin.Context) {
|
||||
"results": filteredResults,
|
||||
})
|
||||
}
|
||||
api.RespondSuccess(c, responseTasks)
|
||||
resp.RespondSuccess(c, responseTasks)
|
||||
}
|
||||
|
||||
func GetTaskById(c *gin.Context) {
|
||||
taskId := c.Param("task_id")
|
||||
if taskId == "" {
|
||||
api.RespondError(c, 400, "Task ID is required")
|
||||
resp.RespondError(c, 400, "Task ID is required")
|
||||
return
|
||||
}
|
||||
task, err := tasks.GetTaskByTaskId(taskId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve task: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve task: "+err.Error())
|
||||
return
|
||||
}
|
||||
if task == nil {
|
||||
api.RespondError(c, 404, "Task not found")
|
||||
resp.RespondError(c, 404, "Task not found")
|
||||
return
|
||||
}
|
||||
results, err := tasks.GetTaskResultsByTaskId(taskId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve task results: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve task results: "+err.Error())
|
||||
return
|
||||
}
|
||||
var filteredResults []gin.H
|
||||
@@ -71,7 +71,7 @@ func GetTaskById(c *gin.Context) {
|
||||
"created_at": r.CreatedAt,
|
||||
})
|
||||
}
|
||||
api.RespondSuccess(c, gin.H{
|
||||
resp.RespondSuccess(c, gin.H{
|
||||
"task_id": task.TaskId,
|
||||
"clients": task.Clients,
|
||||
"command": task.Command,
|
||||
@@ -82,73 +82,73 @@ func GetTaskById(c *gin.Context) {
|
||||
func GetTasksByClientId(c *gin.Context) {
|
||||
clientId := c.Param("uuid")
|
||||
if clientId == "" {
|
||||
api.RespondError(c, 400, "Client ID is required")
|
||||
resp.RespondError(c, 400, "Client ID is required")
|
||||
return
|
||||
}
|
||||
tasks, err := tasks.GetTasksByClientId(clientId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve tasks: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve tasks: "+err.Error())
|
||||
return
|
||||
}
|
||||
if len(tasks) == 0 {
|
||||
api.RespondError(c, 404, "No tasks found for this client")
|
||||
resp.RespondError(c, 404, "No tasks found for this client")
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, tasks)
|
||||
resp.RespondSuccess(c, tasks)
|
||||
}
|
||||
|
||||
func GetSpecificTaskResult(c *gin.Context) {
|
||||
taskId := c.Param("task_id")
|
||||
clientId := c.Param("uuid")
|
||||
if taskId == "" || clientId == "" {
|
||||
api.RespondError(c, 400, "Task ID and Client ID are required")
|
||||
resp.RespondError(c, 400, "Task ID and Client ID are required")
|
||||
return
|
||||
}
|
||||
result, err := tasks.GetSpecificTaskResult(taskId, clientId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve task result: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve task result: "+err.Error())
|
||||
return
|
||||
}
|
||||
if result == nil {
|
||||
api.RespondError(c, 404, "No result found for this task and client")
|
||||
resp.RespondError(c, 404, "No result found for this task and client")
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, result)
|
||||
resp.RespondSuccess(c, result)
|
||||
}
|
||||
|
||||
// Param: task_id
|
||||
func GetTaskResultsByTaskId(c *gin.Context) {
|
||||
taskId := c.Param("task_id")
|
||||
if taskId == "" {
|
||||
api.RespondError(c, 400, "Task ID is required")
|
||||
resp.RespondError(c, 400, "Task ID is required")
|
||||
return
|
||||
}
|
||||
results, err := tasks.GetTaskResultsByTaskId(taskId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve task results: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve task results: "+err.Error())
|
||||
return
|
||||
}
|
||||
if len(results) == 0 {
|
||||
api.RespondError(c, 404, "No results found for this task")
|
||||
resp.RespondError(c, 404, "No results found for this task")
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, results)
|
||||
resp.RespondSuccess(c, results)
|
||||
}
|
||||
|
||||
func GetAllTaskResultByUUID(c *gin.Context) {
|
||||
clientId := c.Param("uuid")
|
||||
if clientId == "" {
|
||||
api.RespondError(c, 400, "Client ID is required")
|
||||
resp.RespondError(c, 400, "Client ID is required")
|
||||
return
|
||||
}
|
||||
results, err := tasks.GetAllTasksResultByUUID(clientId)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to retrieve tasks: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve tasks: "+err.Error())
|
||||
return
|
||||
}
|
||||
if len(results) == 0 {
|
||||
api.RespondError(c, 404, "No tasks found for this client")
|
||||
resp.RespondError(c, 404, "No tasks found for this client")
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, results)
|
||||
resp.RespondSuccess(c, results)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/geoip"
|
||||
@@ -17,10 +17,10 @@ func TestSendMessage(c *gin.Context) {
|
||||
Message: "This is a test message from Komari.",
|
||||
})
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to send message: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to send message: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func TestGeoIp(c *gin.Context) {
|
||||
@@ -34,17 +34,17 @@ func TestGeoIp(c *gin.Context) {
|
||||
}
|
||||
conf, err := conf.GetWithV1Format()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to get configuration: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to get configuration: "+err.Error())
|
||||
return
|
||||
}
|
||||
if !conf.GeoIpEnabled {
|
||||
api.RespondError(c, 400, "GeoIP is not enabled in the configuration.")
|
||||
resp.RespondError(c, 400, "GeoIP is not enabled in the configuration.")
|
||||
return
|
||||
}
|
||||
GeoIpRecord, err := geoip.GetGeoInfo(net.ParseIP(ip))
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to get GeoIP record: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to get GeoIP record: "+err.Error())
|
||||
return
|
||||
}
|
||||
api.RespondSuccess(c, GeoIpRecord)
|
||||
resp.RespondSuccess(c, GeoIpRecord)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -24,32 +24,32 @@ func UploadTheme(c *gin.Context) {
|
||||
// 读取上传的文件内容
|
||||
data, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil || len(data) == 0 {
|
||||
api.RespondError(c, http.StatusBadRequest, "请选择要上传的主题文件")
|
||||
resp.RespondError(c, http.StatusBadRequest, "请选择要上传的主题文件")
|
||||
return
|
||||
}
|
||||
|
||||
// 临时文件名
|
||||
tempFile := filepath.Join(os.TempDir(), "uploaded_theme.zip")
|
||||
if err := os.WriteFile(tempFile, data, 0644); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "保存文件失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "保存文件失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempFile)
|
||||
|
||||
// 检查文件扩展名(这里假定上传的就是zip)
|
||||
if !strings.HasSuffix(strings.ToLower(tempFile), ".zip") {
|
||||
api.RespondError(c, http.StatusBadRequest, "只支持ZIP格式的主题文件")
|
||||
resp.RespondError(c, http.StatusBadRequest, "只支持ZIP格式的主题文件")
|
||||
return
|
||||
}
|
||||
|
||||
// 解压ZIP文件并验证
|
||||
themeInfo, err := extractAndValidateTheme(tempFile)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.RespondSuccessMessage(c, "主题上传成功", themeInfo)
|
||||
resp.RespondSuccessMessage(c, "主题上传成功", themeInfo)
|
||||
}
|
||||
|
||||
// ListThemes 列出所有主题
|
||||
@@ -58,13 +58,13 @@ func ListThemes(c *gin.Context) {
|
||||
|
||||
// 确保主题目录存在
|
||||
if _, err := os.Stat(dataDir); os.IsNotExist(err) {
|
||||
api.RespondSuccess(c, []models.Theme{})
|
||||
resp.RespondSuccess(c, []models.Theme{})
|
||||
return
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(dataDir)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "读取主题目录失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "读取主题目录失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func ListThemes(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
api.RespondSuccess(c, themes)
|
||||
resp.RespondSuccess(c, themes)
|
||||
}
|
||||
|
||||
// DeleteTheme 删除主题
|
||||
@@ -88,12 +88,12 @@ func DeleteTheme(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if req.Short == "default" {
|
||||
api.RespondError(c, http.StatusBadRequest, "默认主题不能删除")
|
||||
resp.RespondError(c, http.StatusBadRequest, "默认主题不能删除")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -101,24 +101,24 @@ func DeleteTheme(c *gin.Context) {
|
||||
|
||||
// 检查主题是否存在
|
||||
if _, err := os.Stat(themeDir); os.IsNotExist(err) {
|
||||
api.RespondError(c, http.StatusNotFound, "主题不存在")
|
||||
resp.RespondError(c, http.StatusNotFound, "主题不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除主题目录
|
||||
if err := os.RemoveAll(themeDir); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "删除主题失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "删除主题失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.RespondSuccessMessage(c, "主题删除成功", nil)
|
||||
resp.RespondSuccessMessage(c, "主题删除成功", nil)
|
||||
}
|
||||
|
||||
// SetTheme 设置主题
|
||||
func SetTheme(c *gin.Context) {
|
||||
themeName := c.Query("theme")
|
||||
if themeName == "" {
|
||||
api.RespondError(c, http.StatusBadRequest, "主题名称不能为空")
|
||||
resp.RespondError(c, http.StatusBadRequest, "主题名称不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ func SetTheme(c *gin.Context) {
|
||||
themeConfigPath := filepath.Join(themeDir, "komari-theme.json")
|
||||
|
||||
if _, err := os.Stat(themeConfigPath); os.IsNotExist(err) {
|
||||
api.RespondError(c, http.StatusNotFound, "主题不存在")
|
||||
resp.RespondError(c, http.StatusNotFound, "主题不存在")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -139,11 +139,11 @@ func SetTheme(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := conf.Update(updateData); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "更新主题设置失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "更新主题设置失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
api.RespondSuccessMessage(c, "主题设置成功", gin.H{"theme": themeName})
|
||||
resp.RespondSuccessMessage(c, "主题设置成功", gin.H{"theme": themeName})
|
||||
}
|
||||
|
||||
// extractAndValidateTheme 解压并验证主题
|
||||
@@ -431,7 +431,7 @@ func UpdateTheme(c *gin.Context) {
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -440,14 +440,14 @@ func UpdateTheme(c *gin.Context) {
|
||||
themeConfigPath := filepath.Join(themeDir, "komari-theme.json")
|
||||
|
||||
if _, err := os.Stat(themeConfigPath); os.IsNotExist(err) {
|
||||
api.RespondError(c, http.StatusNotFound, "主题不存在")
|
||||
resp.RespondError(c, http.StatusNotFound, "主题不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 加载现有主题配置
|
||||
themeInfo, err := loadThemeConfig(themeConfigPath)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "读取主题配置失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "读取主题配置失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -494,14 +494,14 @@ func UpdateTheme(c *gin.Context) {
|
||||
// 相当于: DOWNLOAD_URL=$(curl -s https://api.github.com/repos/owner/repo/releases/latest | jq -r ".assets[0].browser_download_url")
|
||||
gitHubURL, err := getGitHubReleaseDownloadURL(req.GitOwner, req.GitRepo)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "从GitHub获取下载链接失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "从GitHub获取下载链接失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 使用获取到的链接下载主题
|
||||
themeData, err = downloadThemeFromURL(gitHubURL)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "从GitHub下载主题失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "从GitHub下载主题失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
// 保存下载链接,稍后更新到主题配置中
|
||||
@@ -515,14 +515,14 @@ func UpdateTheme(c *gin.Context) {
|
||||
// 这里也应用了自动检测GitHub仓库并下载最新release的功能
|
||||
gitHubURL, err := getGitHubReleaseDownloadURL(owner, repo)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "从GitHub获取下载链接失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "从GitHub获取下载链接失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 使用获取到的链接下载主题
|
||||
themeData, err = downloadThemeFromURL(gitHubURL)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "从GitHub下载主题失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "从GitHub下载主题失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
// 保存GitHub仓库URL,而不是release下载链接,以便将来可以获取最新版本
|
||||
@@ -533,7 +533,7 @@ func UpdateTheme(c *gin.Context) {
|
||||
// 新URL不是GitHub仓库地址,直接尝试下载
|
||||
themeData, err = downloadThemeFromURL(req.URL)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "从新URL下载主题失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "从新URL下载主题失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
// downloadURL = req.URL
|
||||
@@ -543,7 +543,7 @@ func UpdateTheme(c *gin.Context) {
|
||||
|
||||
// 如果没有成功下载主题数据
|
||||
if themeData == nil || len(themeData) == 0 {
|
||||
api.RespondError(c, http.StatusBadRequest, "无法下载主题,请提供有效的URL或GitHub仓库信息")
|
||||
resp.RespondError(c, http.StatusBadRequest, "无法下载主题,请提供有效的URL或GitHub仓库信息")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ func UpdateTheme(c *gin.Context) {
|
||||
// 临时文件名
|
||||
tempFile := filepath.Join(os.TempDir(), "downloaded_theme.zip")
|
||||
if err := os.WriteFile(tempFile, themeData, 0644); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "保存文件失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "保存文件失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempFile)
|
||||
@@ -564,7 +564,7 @@ func UpdateTheme(c *gin.Context) {
|
||||
// 解压ZIP文件并验证
|
||||
updatedThemeInfo, err := extractAndValidateTheme(tempFile)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -576,23 +576,23 @@ func UpdateTheme(c *gin.Context) {
|
||||
// updatedConfigPath := filepath.Join("./data/theme", updatedThemeInfo.Short, "komari-theme.json")
|
||||
// updatedConfigData, err := json.MarshalIndent(updatedThemeInfo, "", " ")
|
||||
// if err != nil {
|
||||
// api.RespondError(c, http.StatusInternalServerError, "生成主题配置失败: "+err.Error())
|
||||
// resp.RespondError(c, http.StatusInternalServerError, "生成主题配置失败: "+err.Error())
|
||||
// return
|
||||
// }
|
||||
|
||||
// if err := os.WriteFile(updatedConfigPath, updatedConfigData, 0644); err != nil {
|
||||
// api.RespondError(c, http.StatusInternalServerError, "更新主题配置文件失败: "+err.Error())
|
||||
// resp.RespondError(c, http.StatusInternalServerError, "更新主题配置文件失败: "+err.Error())
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
api.RespondSuccessMessage(c, "主题更新成功", updatedThemeInfo)
|
||||
resp.RespondSuccessMessage(c, "主题更新成功", updatedThemeInfo)
|
||||
}
|
||||
|
||||
func UpdateThemeSettings(c *gin.Context) {
|
||||
theme := c.Query("theme")
|
||||
if theme == "" || theme == "default" {
|
||||
api.RespondError(c, http.StatusBadRequest, "主题名称不能为空或不能是默认主题")
|
||||
resp.RespondError(c, http.StatusBadRequest, "主题名称不能为空或不能是默认主题")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -600,14 +600,14 @@ func UpdateThemeSettings(c *gin.Context) {
|
||||
|
||||
err := c.ShouldBindJSON(&req)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
db := dbcore.GetDBInstance()
|
||||
|
||||
data, err := json.Marshal(&req)
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "生成主题配置失败: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "生成主题配置失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -615,5 +615,5 @@ func UpdateThemeSettings(c *gin.Context) {
|
||||
db.Where("short = ?", theme).
|
||||
Assign(models.ThemeConfiguration{Short: theme, Data: string(data)}).
|
||||
FirstOrCreate(&themeCfg)
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
)
|
||||
|
||||
@@ -16,31 +16,31 @@ func UploadFavicon(c *gin.Context) {
|
||||
data, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "request body too large") {
|
||||
api.RespondError(c, http.StatusRequestEntityTooLarge, "File too large. Maximum size is 5MB")
|
||||
resp.RespondError(c, http.StatusRequestEntityTooLarge, "File too large. Maximum size is 5MB")
|
||||
} else {
|
||||
api.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
if err := os.WriteFile("./data/favicon.ico", data, 0644); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to save favicon: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to save favicon: "+err.Error())
|
||||
return
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "Favicon uploaded", "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
func DeleteFavicon(c *gin.Context) {
|
||||
if err := os.Remove("./data/favicon.ico"); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
api.RespondError(c, http.StatusNotFound, "Favicon not found")
|
||||
resp.RespondError(c, http.StatusNotFound, "Favicon not found")
|
||||
} else {
|
||||
api.RespondError(c, http.StatusInternalServerError, "Failed to delete favicon: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to delete favicon: "+err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "Favicon deleted", "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -2,17 +2,17 @@ package update
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/geoip"
|
||||
)
|
||||
|
||||
func UpdateMmdbGeoIP(c *gin.Context) {
|
||||
if err := geoip.UpdateDatabase(); err != nil {
|
||||
api.RespondError(c, 500, "Failed to update GeoIP database "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to update GeoIP database "+err.Error())
|
||||
return
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "GeoIP database updated", "info")
|
||||
api.RespondSuccess(c, nil)
|
||||
resp.RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package update
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
)
|
||||
@@ -15,26 +15,26 @@ func UpdateUser(c *gin.Context) {
|
||||
SsoType *string `json:"sso_type"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
api.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
resp.RespondError(c, 400, "Invalid or missing request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
if req.Password == nil && req.Name == nil {
|
||||
api.RespondError(c, 400, "At least one field (username or password) must be provided")
|
||||
resp.RespondError(c, 400, "At least one field (username or password) must be provided")
|
||||
return
|
||||
}
|
||||
if req.Name != nil && len(*req.Name) < 3 {
|
||||
api.RespondError(c, 400, "Username must be at least 3 characters long")
|
||||
resp.RespondError(c, 400, "Username must be at least 3 characters long")
|
||||
return
|
||||
}
|
||||
if req.Password != nil && len(*req.Password) < 6 {
|
||||
api.RespondError(c, 400, "Password must be at least 6 characters long")
|
||||
resp.RespondError(c, 400, "Password must be at least 6 characters long")
|
||||
return
|
||||
}
|
||||
if err := accounts.UpdateUser(req.Uuid, req.Name, req.Password, req.SsoType); err != nil {
|
||||
api.RespondError(c, 500, "Failed to update user: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to update user: "+err.Error())
|
||||
return
|
||||
}
|
||||
uuid, _ := c.Get("uuid")
|
||||
auditlog.Log(c.ClientIP(), uuid.(string), "User updated", "warn")
|
||||
api.RespondSuccess(c, gin.H{"uuid": req.Uuid})
|
||||
resp.RespondSuccess(c, gin.H{"uuid": req.Uuid})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
)
|
||||
|
||||
// 只有一个备份恢复操作在进行
|
||||
@@ -23,7 +23,7 @@ var restoreMutex sync.Mutex
|
||||
func UploadBackup(c *gin.Context) {
|
||||
// 尝试获取锁,如果已有恢复操作在进行,则立即返回错误
|
||||
if !restoreMutex.TryLock() {
|
||||
api.RespondError(c, http.StatusConflict, "Another restore operation is already in progress")
|
||||
resp.RespondError(c, http.StatusConflict, "Another restore operation is already in progress")
|
||||
return
|
||||
}
|
||||
defer restoreMutex.Unlock()
|
||||
@@ -31,27 +31,27 @@ func UploadBackup(c *gin.Context) {
|
||||
// 获取上传的文件
|
||||
file, header, err := c.Request.FormFile("backup")
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusBadRequest, fmt.Sprintf("Error getting uploaded file: %v", err))
|
||||
resp.RespondError(c, http.StatusBadRequest, fmt.Sprintf("Error getting uploaded file: %v", err))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 检查文件是否为zip格式
|
||||
if !strings.HasSuffix(strings.ToLower(header.Filename), ".zip") {
|
||||
api.RespondError(c, http.StatusBadRequest, "Uploaded file must be a ZIP archive")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Uploaded file must be a ZIP archive")
|
||||
return
|
||||
}
|
||||
|
||||
// 确保data目录存在
|
||||
if err := os.MkdirAll("./data", 0755); err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating data directory: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating data directory: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建临时文件保存上传的zip(先校验,再落地到固定位置)
|
||||
tempFile, err := os.CreateTemp("", "backup-upload-*.zip")
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating temporary file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating temporary file: %v", err))
|
||||
return
|
||||
}
|
||||
tempFilePath := tempFile.Name()
|
||||
@@ -61,7 +61,7 @@ func UploadBackup(c *gin.Context) {
|
||||
_, err = io.Copy(tempFile, file)
|
||||
if err != nil {
|
||||
tempFile.Close()
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error saving uploaded file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error saving uploaded file: %v", err))
|
||||
return
|
||||
}
|
||||
tempFile.Close() // 关闭文件以便后续操作
|
||||
@@ -77,11 +77,11 @@ func UploadBackup(c *gin.Context) {
|
||||
}
|
||||
zr.Close()
|
||||
if !hasMarkup {
|
||||
api.RespondError(c, http.StatusBadRequest, "Invalid backup file: missing komari-backup-markup file")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid backup file: missing komari-backup-markup file")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error opening zip file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error opening zip file: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -93,18 +93,18 @@ func UploadBackup(c *gin.Context) {
|
||||
// fallback:拷贝
|
||||
in, err2 := os.Open(tempFilePath)
|
||||
if err2 != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error preparing backup file: %v", err))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error preparing backup file: %v", err))
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
out, err2 := os.Create(finalPath)
|
||||
if err2 != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating target backup file: %v", err2))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error creating target backup file: %v", err2))
|
||||
return
|
||||
}
|
||||
if _, err2 = io.Copy(out, in); err2 != nil {
|
||||
out.Close()
|
||||
api.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error writing target backup file: %v", err2))
|
||||
resp.RespondError(c, http.StatusInternalServerError, fmt.Sprintf("Error writing target backup file: %v", err2))
|
||||
return
|
||||
}
|
||||
out.Close()
|
||||
|
||||
@@ -3,7 +3,7 @@ package client
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gookit/event"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/clients"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
func RegisterClient(c *gin.Context) {
|
||||
auth := c.GetHeader("Authorization")
|
||||
if auth == "" {
|
||||
api.RespondError(c, 403, "Invalid AutoDiscovery Key")
|
||||
resp.RespondError(c, 403, "Invalid AutoDiscovery Key")
|
||||
return
|
||||
}
|
||||
cfg, err := conf.GetWithV1Format()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to get configuration: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to get configuration: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func RegisterClient(c *gin.Context) {
|
||||
len(cfg.AutoDiscoveryKey) < 12 ||
|
||||
"Bearer "+cfg.AutoDiscoveryKey != auth {
|
||||
|
||||
api.RespondError(c, 403, "Invalid AutoDiscovery Key")
|
||||
resp.RespondError(c, 403, "Invalid AutoDiscovery Key")
|
||||
return
|
||||
}
|
||||
name := c.Query("name")
|
||||
@@ -36,12 +36,12 @@ func RegisterClient(c *gin.Context) {
|
||||
name = "Auto-" + name
|
||||
uuid, token, err := clients.CreateClientWithName(name)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to create client: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to create client: "+err.Error())
|
||||
return
|
||||
}
|
||||
event.Trigger(eventType.ClientCreated, event.M{
|
||||
"client": uuid,
|
||||
"token": token,
|
||||
})
|
||||
api.RespondSuccess(c, gin.H{"uuid": uuid, "token": token})
|
||||
resp.RespondSuccess(c, gin.H{"uuid": uuid, "token": token})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gookit/event"
|
||||
"github.com/gorilla/websocket"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/vars"
|
||||
"github.com/komari-monitor/komari/internal/common"
|
||||
"github.com/komari-monitor/komari/internal/database/clients"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -217,7 +217,7 @@ func processMessage(conn *ws.SafeConn, message []byte, uuid string) {
|
||||
}
|
||||
|
||||
func SaveClientReport(uuid string, report common.Report) error {
|
||||
reports, _ := api.Records.Get(uuid)
|
||||
reports, _ := vars.Records.Get(uuid)
|
||||
if reports == nil {
|
||||
reports = []common.Report{}
|
||||
}
|
||||
@@ -225,7 +225,7 @@ func SaveClientReport(uuid string, report common.Report) error {
|
||||
report.CPU.Usage = 0.01
|
||||
}
|
||||
reports = append(reports.([]common.Report), report)
|
||||
api.Records.Set(uuid, reports, cache.DefaultExpiration)
|
||||
vars.Records.Set(uuid, reports, cache.DefaultExpiration)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/terminal"
|
||||
)
|
||||
|
||||
func EstablishConnection(c *gin.Context) {
|
||||
session_id := c.Query("id")
|
||||
session, exists := api.TerminalSessions[session_id]
|
||||
session, exists := terminal.TerminalSessions[session_id]
|
||||
if !exists || session == nil || session.Browser == nil {
|
||||
c.JSON(404, gin.H{"status": "error", "error": "Session not found"})
|
||||
return
|
||||
@@ -27,22 +27,22 @@ func EstablishConnection(c *gin.Context) {
|
||||
}
|
||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
api.TerminalSessionsMutex.Lock()
|
||||
terminal.TerminalSessionsMutex.Lock()
|
||||
if session.Browser != nil {
|
||||
session.Browser.Close()
|
||||
}
|
||||
delete(api.TerminalSessions, session_id)
|
||||
api.TerminalSessionsMutex.Unlock()
|
||||
delete(terminal.TerminalSessions, session_id)
|
||||
terminal.TerminalSessionsMutex.Unlock()
|
||||
return
|
||||
}
|
||||
session.Agent = conn
|
||||
conn.SetCloseHandler(func(code int, text string) error {
|
||||
delete(api.TerminalSessions, session_id)
|
||||
delete(terminal.TerminalSessions, session_id)
|
||||
// 通知 Browser 关闭终端连接
|
||||
if session.Browser != nil {
|
||||
session.Browser.Close()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
go api.ForwardTerminal(session_id)
|
||||
go terminal.ForwardTerminal(session_id)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package internal
|
||||
package api_v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/admin"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/admin/clipboard"
|
||||
log_api "github.com/komari-monitor/komari/internal/api_v1/admin/log"
|
||||
@@ -11,10 +11,27 @@ import (
|
||||
"github.com/komari-monitor/komari/internal/api_v1/admin/update"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/client"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/record"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/task"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/terminal"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
func init() {
|
||||
event.On(eventType.ServerInitializeStart, event.ListenerFunc(func(e event.Event) error {
|
||||
r := e.Get("engine").(*gin.Engine)
|
||||
config, _ := conf.GetWithV1Format()
|
||||
LoadApiV1Routes(r, config)
|
||||
return nil
|
||||
}), event.Normal)
|
||||
|
||||
event.On(eventType.SchedulerEveryMinute, event.ListenerFunc(func(e event.Event) error {
|
||||
SaveClientReportToDB()
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func LoadApiV1Routes(r *gin.Engine, conf conf.V1Struct) {
|
||||
r.Use(func(c *gin.Context) {
|
||||
if len(c.Request.URL.Path) >= 4 && c.Request.URL.Path[:4] == "/api" {
|
||||
@@ -23,30 +40,29 @@ func LoadApiV1Routes(r *gin.Engine, conf conf.V1Struct) {
|
||||
c.Next()
|
||||
})
|
||||
|
||||
r.Use(api.PrivateSiteMiddleware())
|
||||
r.Use(PrivateSiteMiddleware())
|
||||
|
||||
r.Any("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
// #region 公开路由
|
||||
r.POST("/api/login", api.Login)
|
||||
r.GET("/api/me", api.GetMe)
|
||||
r.GET("/api/clients", api.GetClients)
|
||||
r.GET("/api/nodes", api.GetNodesInformation)
|
||||
r.GET("/api/public", api.GetPublicSettings)
|
||||
r.GET("/api/oauth", api.OAuth)
|
||||
r.GET("/api/oauth_callback", api.OAuthCallback)
|
||||
r.GET("/api/logout", api.Logout)
|
||||
r.GET("/api/version", api.GetVersion)
|
||||
r.GET("/api/recent/:uuid", api.GetClientRecentRecords)
|
||||
|
||||
r.POST("/api/login", Login)
|
||||
r.GET("/api/me", GetMe)
|
||||
r.GET("/api/clients", GetClients)
|
||||
r.GET("/api/nodes", GetNodesInformation)
|
||||
r.GET("/api/public", GetPublicSettings)
|
||||
r.GET("/api/oauth", OAuth)
|
||||
r.GET("/api/oauth_callback", OAuthCallback)
|
||||
r.GET("/api/logout", Logout)
|
||||
r.GET("/api/version", resp.GetVersion)
|
||||
r.GET("/api/recent/:uuid", GetClientRecentRecords)
|
||||
r.GET("/api/records/load", record.GetRecordsByUUID)
|
||||
r.GET("/api/records/ping", record.GetPingRecords)
|
||||
r.GET("/api/task/ping", task.GetPublicPingTasks)
|
||||
|
||||
// #region Agent
|
||||
r.POST("/api/clients/register", client.RegisterClient)
|
||||
tokenAuthrized := r.Group("/api/clients", api.TokenAuthMiddleware())
|
||||
tokenAuthrized := r.Group("/api/clients", TokenAuthMiddleware())
|
||||
{
|
||||
tokenAuthrized.GET("/report", client.WebSocketReport) // websocket
|
||||
tokenAuthrized.POST("/uploadBasicInfo", client.UploadBasicInfo)
|
||||
@@ -55,7 +71,7 @@ func LoadApiV1Routes(r *gin.Engine, conf conf.V1Struct) {
|
||||
tokenAuthrized.POST("/task/result", client.TaskResult)
|
||||
}
|
||||
// #region 管理员
|
||||
adminAuthrized := r.Group("/api/admin", api.AdminAuthMiddleware())
|
||||
adminAuthrized := r.Group("/api/admin", AdminAuthMiddleware())
|
||||
{
|
||||
adminAuthrized.GET("/download/backup", admin.DownloadBackup)
|
||||
adminAuthrized.POST("/upload/backup", admin.UploadBackup)
|
||||
@@ -114,7 +130,7 @@ func LoadApiV1Routes(r *gin.Engine, conf conf.V1Struct) {
|
||||
clientGroup.GET("/:uuid/token", admin.GetClientToken)
|
||||
clientGroup.POST("/order", admin.OrderWeight)
|
||||
// client terminal
|
||||
clientGroup.GET("/:uuid/terminal", api.RequestTerminal)
|
||||
clientGroup.GET("/:uuid/terminal", terminal.RequestTerminal)
|
||||
}
|
||||
|
||||
// records
|
||||
@@ -2,6 +2,8 @@ package api_v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/vars"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -11,7 +13,7 @@ func GetClientRecentRecords(c *gin.Context) {
|
||||
uuid := c.Param("uuid")
|
||||
|
||||
if uuid == "" {
|
||||
RespondError(c, 400, "UUID is required")
|
||||
resp.RespondError(c, 400, "UUID is required")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,11 +36,11 @@ func GetClientRecentRecords(c *gin.Context) {
|
||||
}
|
||||
|
||||
if hiddenMap[uuid] {
|
||||
RespondError(c, 400, "UUID is required") //防止未登录用户获取隐藏客户端数据
|
||||
resp.RespondError(c, 400, "UUID is required") //防止未登录用户获取隐藏客户端数据
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
records, _ := Records.Get(uuid)
|
||||
RespondSuccess(c, records)
|
||||
records, _ := vars.Records.Get(uuid)
|
||||
resp.RespondSuccess(c, records)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
@@ -23,29 +24,29 @@ type LoginRequest struct {
|
||||
func Login(c *gin.Context) {
|
||||
conf, _ := conf.GetWithV1Format()
|
||||
if conf.DisablePasswordLogin {
|
||||
RespondError(c, http.StatusForbidden, "Password login is disabled")
|
||||
resp.RespondError(c, http.StatusForbidden, "Password login is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusBadRequest, "Invalid request body: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
var data LoginRequest
|
||||
err = json.Unmarshal(bodyBytes, &data)
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusBadRequest, "Invalid request body: "+err.Error())
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
if data.Username == "" || data.Password == "" {
|
||||
RespondError(c, http.StatusBadRequest, "Invalid request body: Username and password are required")
|
||||
resp.RespondError(c, http.StatusBadRequest, "Invalid request body: Username and password are required")
|
||||
return
|
||||
}
|
||||
|
||||
uuid, success := accounts.CheckPassword(data.Username, data.Password)
|
||||
if !success {
|
||||
RespondError(c, http.StatusUnauthorized, "Invalid credentials")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "Invalid credentials")
|
||||
event.Trigger(eventType.LoginFailed, event.M{
|
||||
"username": data.Username,
|
||||
"method": "password",
|
||||
@@ -61,23 +62,23 @@ func Login(c *gin.Context) {
|
||||
user, _ := accounts.GetUserByUUID(uuid)
|
||||
if user.TwoFactor != "" { // 开启了2FA
|
||||
if data.TwoFa == "" {
|
||||
RespondError(c, http.StatusUnauthorized, "2FA code is required")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "2FA code is required")
|
||||
return
|
||||
}
|
||||
if ok, err := accounts.Verify2Fa(uuid, data.TwoFa); err != nil || !ok {
|
||||
RespondError(c, http.StatusUnauthorized, "Invalid 2FA code")
|
||||
resp.RespondError(c, http.StatusUnauthorized, "Invalid 2FA code")
|
||||
return
|
||||
}
|
||||
}
|
||||
// Create session
|
||||
session, err := accounts.CreateSession(uuid, 2592000, c.Request.UserAgent(), c.ClientIP(), "password")
|
||||
if err != nil {
|
||||
RespondError(c, http.StatusInternalServerError, "Failed to create session: "+err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, "Failed to create session: "+err.Error())
|
||||
return
|
||||
}
|
||||
c.SetCookie("session_token", session, 2592000, "/", "", false, true)
|
||||
auditlog.Log(c.ClientIP(), uuid, "logged in (password)", "login")
|
||||
RespondSuccess(c, gin.H{"set-cookie": gin.H{"session_token": session}})
|
||||
resp.RespondSuccess(c, gin.H{"set-cookie": gin.H{"session_token": session}})
|
||||
event.Trigger(eventType.UserLogin, event.M{
|
||||
"username": data.Username,
|
||||
"method": "password",
|
||||
|
||||
@@ -2,6 +2,7 @@ package api_v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/komari-monitor/komari/internal/database/clients"
|
||||
)
|
||||
@@ -9,7 +10,7 @@ import (
|
||||
func GetNodesInformation(c *gin.Context) {
|
||||
clientList, err := clients.GetAllClientBasicInfo()
|
||||
if err != nil {
|
||||
RespondError(c, 500, "Failed to retrieve client information: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to retrieve client information: "+err.Error())
|
||||
return
|
||||
}
|
||||
isLogin := false
|
||||
@@ -35,5 +36,5 @@ func GetNodesInformation(c *gin.Context) {
|
||||
}
|
||||
clientList = clientList[:j]
|
||||
|
||||
RespondSuccess(c, clientList)
|
||||
resp.RespondSuccess(c, clientList)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ package api_v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database"
|
||||
)
|
||||
|
||||
func GetPublicSettings(c *gin.Context) {
|
||||
p, e := database.GetPublicInfo()
|
||||
if e != nil {
|
||||
RespondError(c, 500, e.Error())
|
||||
resp.RespondError(c, 500, e.Error())
|
||||
return
|
||||
}
|
||||
RespondSuccess(c, p)
|
||||
resp.RespondSuccess(c, p)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/accounts"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
@@ -37,14 +37,14 @@ func GetRecordsByUUID(c *gin.Context) {
|
||||
}
|
||||
|
||||
if hiddenMap[uuid] {
|
||||
api.RespondError(c, 400, "UUID is required") //防止未登录用户获取隐藏客户端数据
|
||||
resp.RespondError(c, 400, "UUID is required") //防止未登录用户获取隐藏客户端数据
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hours := c.Query("hours")
|
||||
if uuid == "" {
|
||||
api.RespondError(c, 400, "UUID is required")
|
||||
resp.RespondError(c, 400, "UUID is required")
|
||||
return
|
||||
}
|
||||
if hours == "" {
|
||||
@@ -53,7 +53,7 @@ func GetRecordsByUUID(c *gin.Context) {
|
||||
|
||||
hoursInt, err := strconv.Atoi(hours)
|
||||
if err != nil {
|
||||
api.RespondError(c, 400, "Invalid hours parameter")
|
||||
resp.RespondError(c, 400, "Invalid hours parameter")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ func GetRecordsByUUID(c *gin.Context) {
|
||||
}
|
||||
|
||||
if !validLoadTypes[loadType] {
|
||||
api.RespondError(c, 400, "Invalid load_type parameter")
|
||||
resp.RespondError(c, 400, "Invalid load_type parameter")
|
||||
return
|
||||
}
|
||||
|
||||
clientRecords, err := records.GetRecordsByClientAndTime(uuid, time.Now().Add(-time.Duration(hoursInt)*time.Hour), time.Now())
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to fetch records: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to fetch records: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func GetRecordsByUUID(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
api.RespondSuccess(c, response)
|
||||
resp.RespondSuccess(c, response)
|
||||
}
|
||||
|
||||
// filterRecordsByLoadType 根据 load_type 过滤记录,只返回相关字段
|
||||
@@ -195,7 +195,7 @@ func GetPingRecords(c *gin.Context) {
|
||||
|
||||
// 必须提供 uuid 或 task_id 其中至少一个
|
||||
if uuid == "" && taskIdStr == "" {
|
||||
api.RespondError(c, 400, "UUID or task_id is required")
|
||||
resp.RespondError(c, 400, "UUID or task_id is required")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ func GetPingRecords(c *gin.Context) {
|
||||
}
|
||||
if uuid != "" {
|
||||
if hiddenMap[uuid] {
|
||||
api.RespondSuccess(c, response) // 对于尝试获取隐藏uuid一键哈气
|
||||
resp.RespondSuccess(c, response) // 对于尝试获取隐藏uuid一键哈气
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func GetPingRecords(c *gin.Context) {
|
||||
if taskIdStr != "" {
|
||||
taskId, err = strconv.Atoi(taskIdStr)
|
||||
if err != nil {
|
||||
api.RespondError(c, 400, "Invalid task_id parameter")
|
||||
resp.RespondError(c, 400, "Invalid task_id parameter")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ func GetPingRecords(c *gin.Context) {
|
||||
// 查询记录,现在支持 uuid + task_id 组合查询
|
||||
records, err = tasks.GetPingRecords(uuid, taskId, startTime, endTime)
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to fetch ping records: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to fetch ping records: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ func GetPingRecords(c *gin.Context) {
|
||||
// 获取所有 pingTasks
|
||||
pingTasks, err := tasks.GetAllPingTasks()
|
||||
if err != nil {
|
||||
api.RespondError(c, 500, "Failed to fetch ping tasks: "+err.Error())
|
||||
resp.RespondError(c, 500, "Failed to fetch ping tasks: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -443,5 +443,5 @@ func GetPingRecords(c *gin.Context) {
|
||||
}
|
||||
|
||||
response.Count = len(response.Records) // 计算最后结果保持计数一致
|
||||
api.RespondSuccess(c, response)
|
||||
resp.RespondSuccess(c, response)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package resp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// Respond sends a standardized JSON response.
|
||||
func Respond(c *gin.Context, httpStatus int, status string, message string, data interface{}) {
|
||||
c.JSON(httpStatus, Response{Status: status, Message: message, Data: data})
|
||||
}
|
||||
|
||||
// RespondSuccess sends a success response with data.
|
||||
func RespondSuccess(c *gin.Context, data interface{}) {
|
||||
Respond(c, http.StatusOK, "success", "", data)
|
||||
}
|
||||
|
||||
// RespondSuccessMessage sends a success response with message and data.
|
||||
func RespondSuccessMessage(c *gin.Context, message string, data interface{}) {
|
||||
Respond(c, http.StatusOK, "success", message, data)
|
||||
}
|
||||
|
||||
// RespondError sends an error response with message.
|
||||
func RespondError(c *gin.Context, httpStatus int, message string) {
|
||||
Respond(c, httpStatus, "error", message, nil)
|
||||
}
|
||||
func GetVersion(c *gin.Context) {
|
||||
RespondSuccess(c, gin.H{
|
||||
"version": conf.Version,
|
||||
"hash": conf.CommitHash,
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
api "github.com/komari-monitor/komari/internal/api_v1"
|
||||
"github.com/komari-monitor/komari/internal/api_v1/resp"
|
||||
"github.com/komari-monitor/komari/internal/database/tasks"
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ type PublicPingTask struct {
|
||||
func GetPublicPingTasks(c *gin.Context) {
|
||||
tasks, err := tasks.GetAllPingTasks()
|
||||
if err != nil {
|
||||
api.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
resp.RespondError(c, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,5 +34,5 @@ func GetPublicPingTasks(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
api.RespondSuccess(c, publicTasks)
|
||||
resp.RespondSuccess(c, publicTasks)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
package api_v1
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/database/clients"
|
||||
"github.com/komari-monitor/komari/internal/ws"
|
||||
"github.com/komari-monitor/komari/pkg/utils"
|
||||
)
|
||||
|
||||
type TerminalSession struct {
|
||||
UUID string
|
||||
UserUUID string
|
||||
Browser *websocket.Conn
|
||||
Agent *websocket.Conn
|
||||
RequesterIp string
|
||||
}
|
||||
|
||||
var TerminalSessionsMutex = &sync.Mutex{}
|
||||
var TerminalSessions = make(map[string]*TerminalSession)
|
||||
|
||||
func RequestTerminal(c *gin.Context) {
|
||||
uuid := c.Param("uuid")
|
||||
user_uuid, _ := c.Get("uuid")
|
||||
@@ -0,0 +1,11 @@
|
||||
package vars
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var (
|
||||
Records = cache.New(1*time.Minute, 1*time.Minute)
|
||||
)
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
}))
|
||||
}
|
||||
+35
-18
@@ -2,10 +2,14 @@ package conf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"log/slog"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/cmd/flags"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
@@ -38,17 +42,25 @@ func Override(cst Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(flags.ConfigFile, b, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldConf := *Conf
|
||||
Conf = &cst
|
||||
event.Trigger(eventType.ConfigUpdated, event.M{
|
||||
err, _ = event.Trigger(eventType.ConfigUpdated, event.M{
|
||||
"old": oldConf,
|
||||
"new": cst,
|
||||
})
|
||||
return nil
|
||||
if err != nil {
|
||||
// 撤回配置修改
|
||||
Conf = &oldConf
|
||||
b, _ = json.MarshalIndent(oldConf, "", " ")
|
||||
|
||||
auditlog.EventLog("error", fmt.Sprintf("Configuration update reverted due to error in ConfigUpdated event: %v", err))
|
||||
slog.Error("Configuration update reverted due to error in ConfigUpdated event.", slog.Any("error", err))
|
||||
}
|
||||
if err := os.WriteFile(flags.ConfigFile, b, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func SavePartial(cst map[string]interface{}) error {
|
||||
@@ -96,19 +108,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
|
||||
@@ -249,3 +248,21 @@ func deepMerge(dst, src map[string]interface{}) map[string]interface{} {
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// FromEvent 从事件对象中提取旧配置和新配置 returns (old,new,error)。
|
||||
func FromEvent(e event.Event) (Config, Config, error) {
|
||||
oldVal := e.Get("old")
|
||||
newVal := e.Get("new")
|
||||
|
||||
oldConf, ok := oldVal.(Config)
|
||||
if !ok {
|
||||
return Config{}, Config{}, fmt.Errorf("FromEvent: 'old' key value is not of type Config. Got: %T", oldVal)
|
||||
}
|
||||
|
||||
newConf, ok := newVal.(Config)
|
||||
if !ok {
|
||||
return Config{}, Config{}, fmt.Errorf("FromEvent: 'new' key value is not of type Config. Got: %T", newVal)
|
||||
}
|
||||
|
||||
return oldConf, newConf, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
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() {
|
||||
Conf = &Config{}
|
||||
event.On(eventType.ProcessStart, event.ListenerFunc(func(e event.Event) error {
|
||||
b, err := os.ReadFile(flags.ConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cst := &Config{}
|
||||
if err := json.Unmarshal(b, cst); err != nil {
|
||||
return err
|
||||
}
|
||||
Conf = cst
|
||||
return nil
|
||||
}), event.Max+2)
|
||||
|
||||
event.On(eventType.ServerInitializeDone, event.ListenerFunc(func(e event.Event) error {
|
||||
event.Trigger(eventType.ConfigUpdated, event.M{
|
||||
"old": Config{},
|
||||
"new": *Conf,
|
||||
})
|
||||
return nil
|
||||
}), event.Low)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
func init() {
|
||||
event.On(eventType.ServerInitializeStart, event.ListenerFunc(func(e event.Event) error {
|
||||
var count int64 = 0
|
||||
if dbcore.GetDBInstance().Model(&models.User{}).Count(&count); count == 0 {
|
||||
user, passwd, err := CreateDefaultAdminAccount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Default admin account created. Username:", user, ", Password:", passwd)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package auditlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
@@ -25,6 +26,14 @@ func EventLog(eventType, message string) {
|
||||
Log("", "", message, eventType)
|
||||
}
|
||||
|
||||
func InternalInfof(message string, v ...interface{}) {
|
||||
Log("", "", fmt.Sprintf(message, v...), "info")
|
||||
}
|
||||
|
||||
func InternalErrorf(message string, v ...interface{}) {
|
||||
Log("", "", fmt.Sprintf(message, v...), "error")
|
||||
}
|
||||
|
||||
// Delete logs older than 30 days
|
||||
func RemoveOldLogs() {
|
||||
db := dbcore.GetDBInstance()
|
||||
|
||||
@@ -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
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package dbcore
|
||||
|
||||
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 {
|
||||
_ = GetDBInstance()
|
||||
return nil
|
||||
}), event.Max+1)
|
||||
}
|
||||
@@ -59,10 +59,5 @@ func SaveLoadNotification(record models.LoadNotification) error {
|
||||
}
|
||||
|
||||
func ReloadLoadNotificationSchedule() error {
|
||||
db := dbcore.GetDBInstance()
|
||||
var loadNotifications []models.LoadNotification
|
||||
if err := db.Find(&loadNotifications).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return notifier.ReloadLoadNotificationSchedule(loadNotifications)
|
||||
return notifier.ReloadLoadNotification()
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -21,12 +21,12 @@ const (
|
||||
|
||||
LoginFailed = "user.login.failed" // 用户登录失败
|
||||
|
||||
ConfigUpdated = "config.updated" // 配置更改
|
||||
ConfigUpdated = "config.updated" // 配置更改。当服务器第一次启动载入配置时也会触发此事件(由 ServerInitializeDone 事件联动),其中 old 全部为go类型零值, new 为载入的配置内容,若在配置载入前读取Conf,返回的将是零值配置。当出现错误时,配置修改将会被撤回。
|
||||
|
||||
ProcessStart = "process.start" // 进程启动
|
||||
ProcessStart = "process.start" // 进程启动,如果在此事件的监听器中返回错误,进程将退出。注意:此时数据库、配置进行初始化,不应该在此事件的监听器中执行需要数据库或配置的操作,即使需要进行操作数据库和配置,请将优先级设置为event.Max以下
|
||||
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" // 通知发送失败
|
||||
|
||||
+52
-35
@@ -7,7 +7,9 @@ import (
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
@@ -22,44 +24,31 @@ type GeoInfo struct {
|
||||
func init() {
|
||||
CurrentProvider = &EmptyProvider{}
|
||||
geoCache = cache.New(48*time.Hour, 1*time.Hour)
|
||||
}
|
||||
|
||||
// GeoIPService 接口定义了获取地理位置信息的核心方法。
|
||||
// 任何实现此接口的类型都可以作为地理位置服务提供者。
|
||||
type GeoIPService interface {
|
||||
Name() string
|
||||
|
||||
GetGeoInfo(ip net.IP) (*GeoInfo, error)
|
||||
|
||||
UpdateDatabase() error
|
||||
|
||||
Close() error
|
||||
}
|
||||
|
||||
func GetRegionUnicodeEmoji(isoCode string) string {
|
||||
if len(isoCode) != 2 {
|
||||
return ""
|
||||
}
|
||||
isoCode = strings.ToUpper(isoCode)
|
||||
|
||||
if !unicode.IsLetter(rune(isoCode[0])) || !unicode.IsLetter(rune(isoCode[1])) {
|
||||
return ""
|
||||
}
|
||||
|
||||
rune1 := rune(0x1F1E6 + (rune(isoCode[0]) - 'A'))
|
||||
rune2 := rune(0x1F1E6 + (rune(isoCode[1]) - 'A'))
|
||||
return string(rune1) + string(rune2)
|
||||
}
|
||||
|
||||
func InitGeoIp() {
|
||||
config, err := conf.GetWithV1Format()
|
||||
err := SetProvider("empty")
|
||||
if err != nil {
|
||||
panic("Failed to get configuration for GeoIP: " + err.Error())
|
||||
log.Printf("Failed to set initial GeoIP provider: %v", err)
|
||||
}
|
||||
if !config.GeoIpEnabled {
|
||||
return
|
||||
}
|
||||
switch config.GeoIpProvider {
|
||||
|
||||
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
|
||||
oldConf, newConf, err := conf.FromEvent(e)
|
||||
if oldConf.GeoIp.GeoIpProvider == newConf.GeoIp.GeoIpProvider {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse config from event: %v", err)
|
||||
return err
|
||||
}
|
||||
err = SetProvider(newConf.GeoIp.GeoIpProvider)
|
||||
if err != nil {
|
||||
log.Printf("Failed to set GeoIP provider: %v", err)
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func SetProvider(provider string) error {
|
||||
switch provider {
|
||||
case "mmdb":
|
||||
NewCurrentProvider, err := NewMaxMindGeoIPService()
|
||||
if err != nil {
|
||||
@@ -110,6 +99,34 @@ func InitGeoIp() {
|
||||
default:
|
||||
CurrentProvider = &EmptyProvider{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GeoIPService 接口定义了获取地理位置信息的核心方法。
|
||||
// 任何实现此接口的类型都可以作为地理位置服务提供者。
|
||||
type GeoIPService interface {
|
||||
Name() string
|
||||
|
||||
GetGeoInfo(ip net.IP) (*GeoInfo, error)
|
||||
|
||||
UpdateDatabase() error
|
||||
|
||||
Close() error
|
||||
}
|
||||
|
||||
func GetRegionUnicodeEmoji(isoCode string) string {
|
||||
if len(isoCode) != 2 {
|
||||
return ""
|
||||
}
|
||||
isoCode = strings.ToUpper(isoCode)
|
||||
|
||||
if !unicode.IsLetter(rune(isoCode[0])) || !unicode.IsLetter(rune(isoCode[1])) {
|
||||
return ""
|
||||
}
|
||||
|
||||
rune1 := rune(0x1F1E6 + (rune(isoCode[0]) - 'A'))
|
||||
rune2 := rune(0x1F1E6 + (rune(isoCode[1]) - 'A'))
|
||||
return string(rune1) + string(rune2)
|
||||
}
|
||||
|
||||
func GetGeoInfo(ip net.IP) (*GeoInfo, error) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package messageSender
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
func init() {
|
||||
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
|
||||
oldConf, newConf, err := conf.FromEvent(e)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse config from event: %v", err)
|
||||
return err
|
||||
}
|
||||
if newConf.Notification.NotificationMethod != oldConf.Notification.NotificationMethod {
|
||||
Initialize()
|
||||
}
|
||||
return nil
|
||||
}), event.Max)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package server
|
||||
package nezha
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -33,35 +33,25 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
func StartNezhaGRPCServer(listen string) {
|
||||
func init() {
|
||||
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
|
||||
New := e.Get("new").(conf.Config)
|
||||
Old := e.Get("old").(conf.Config)
|
||||
if New.Compact.Nezha.NezhaCompatEnabled != Old.Compact.Nezha.NezhaCompatEnabled {
|
||||
Old, New, _ := conf.FromEvent(e)
|
||||
if New.Compact.Nezha.NezhaCompatEnabled != Old.Compact.Nezha.NezhaCompatEnabled || New.Compact.Nezha.NezhaCompatListen != Old.Compact.Nezha.NezhaCompatListen {
|
||||
if New.Compact.Nezha.NezhaCompatEnabled {
|
||||
StopNezhaCompat()
|
||||
if err := StartNezhaCompat(New.Compact.Nezha.NezhaCompatListen); err != nil {
|
||||
log.Printf("start Nezha compat server error: %v", err)
|
||||
auditlog.EventLog("error", fmt.Sprintf("start Nezha compat server error: %v", err))
|
||||
return fmt.Errorf("start Nezha compat server error: %w", err)
|
||||
}
|
||||
event.Trigger(eventType.ServerListenGrpcStart, nil)
|
||||
} else {
|
||||
if err := StopNezhaCompat(); err != nil {
|
||||
log.Printf("stop Nezha compat server error: %v", err)
|
||||
auditlog.EventLog("error", fmt.Sprintf("stop Nezha compat server error: %v", err))
|
||||
return fmt.Errorf("stop Nezha compat server error: %w", err)
|
||||
}
|
||||
event.Trigger(eventType.ServerListenGrpcStop, nil)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
|
||||
if listen == "" {
|
||||
return
|
||||
}
|
||||
if err := StartNezhaCompat(listen); err != nil {
|
||||
log.Printf("Nezha compat server error: %v", err)
|
||||
auditlog.EventLog("error", fmt.Sprintf("Nezha compat server error: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Manual start/stop support ----
|
||||
@@ -131,7 +121,7 @@ func StopNezhaCompat() error {
|
||||
nezhaOnceM.Lock()
|
||||
defer nezhaOnceM.Unlock()
|
||||
if nezhaSrv == nil {
|
||||
return errors.New("nezha compat server not running")
|
||||
return nil
|
||||
}
|
||||
// 强制立即断开所有连接与流,不等待在途 RPC 完成。
|
||||
nezhaSrv.Stop()
|
||||
@@ -0,0 +1,18 @@
|
||||
package notifier
|
||||
|
||||
import (
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
func init() {
|
||||
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
|
||||
}))
|
||||
}
|
||||
@@ -218,7 +218,11 @@ func updateLastNotified(taskId uint, notifyTime time.Time) {
|
||||
}
|
||||
}
|
||||
|
||||
// ReloadLoadNotificationSchedule 加载或重载时间表
|
||||
func ReloadLoadNotificationSchedule(loadNotifications []models.LoadNotification) error {
|
||||
func ReloadLoadNotification() error {
|
||||
db := dbcore.GetDBInstance()
|
||||
var loadNotifications []models.LoadNotification
|
||||
if err := db.Find(&loadNotifications).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return LoadNotificationManager.Reload(loadNotifications)
|
||||
}
|
||||
|
||||
+28
-23
@@ -6,16 +6,18 @@ import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database"
|
||||
"github.com/komari-monitor/komari/internal/database/auditlog"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
"github.com/komari-monitor/komari/internal/oauth/factory"
|
||||
)
|
||||
|
||||
var (
|
||||
currentProvider factory.IOidcProvider
|
||||
mu = sync.Mutex{}
|
||||
once = sync.Once{}
|
||||
)
|
||||
|
||||
func CurrentProvider() factory.IOidcProvider {
|
||||
@@ -47,8 +49,8 @@ func LoadProvider(name string, configJson string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Initialize() error {
|
||||
once.Do(func() {
|
||||
func init() {
|
||||
event.On(eventType.ServerInitializeStart, event.ListenerFunc(func(e event.Event) error {
|
||||
all := factory.GetAllOidcProviders()
|
||||
for _, provider := range all {
|
||||
if _, err := database.GetOidcConfigByName(provider.GetName()); err == nil {
|
||||
@@ -58,33 +60,36 @@ func Initialize() error {
|
||||
config := provider.GetConfiguration()
|
||||
configBytes, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
log.Printf("Failed to marshal config for provider %s: %v", provider.GetName(), err)
|
||||
return
|
||||
return fmt.Errorf("failed to marshal config for provider %s: %v", provider.GetName(), err)
|
||||
}
|
||||
if err := database.SaveOidcConfig(&models.OidcProvider{
|
||||
Name: provider.GetName(),
|
||||
Addition: string(configBytes),
|
||||
}); err != nil {
|
||||
log.Printf("Failed to save default config for provider %s: %v", provider.GetName(), err)
|
||||
return
|
||||
return fmt.Errorf("failed to save default config for provider %s: %v", provider.GetName(), err)
|
||||
}
|
||||
}
|
||||
})
|
||||
cfg, _ := conf.GetWithV1Format()
|
||||
if cfg.OAuthProvider == "" || cfg.OAuthProvider == "none" {
|
||||
LoadProvider("empty", "{}")
|
||||
return nil
|
||||
}
|
||||
provider, err := database.GetOidcConfigByName(cfg.OAuthProvider)
|
||||
if err != nil {
|
||||
// 如果没有找到配置,使用empty provider
|
||||
LoadProvider("empty", "{}")
|
||||
}))
|
||||
|
||||
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
|
||||
oldConf, newConf, err := conf.FromEvent(e)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse config from event: %v", err)
|
||||
}
|
||||
|
||||
if newConf.Login.OAuthProvider != oldConf.Login.OAuthProvider {
|
||||
oidcProvider, err := database.GetOidcConfigByName(newConf.Login.OAuthProvider)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get OIDC provider config: %v", err)
|
||||
} else {
|
||||
log.Printf("Using %s as OIDC provider", oidcProvider.Name)
|
||||
}
|
||||
err = LoadProvider(oidcProvider.Name, oidcProvider.Addition)
|
||||
if err != nil {
|
||||
auditlog.EventLog("error", fmt.Sprintf("Failed to load OIDC provider: %v", err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = LoadProvider(provider.Name, provider.Addition)
|
||||
if err != nil {
|
||||
log.Printf("Failed to load OIDC provider %s: %v", provider.Name, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
+32
-27
@@ -3,36 +3,41 @@ package patch
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/database/dbcore"
|
||||
"github.com/komari-monitor/komari/internal/database/models"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
)
|
||||
|
||||
func ApplyPatch() {
|
||||
db := dbcore.GetDBInstance()
|
||||
// 0.0.5 迁移ClientInfo
|
||||
if db.Migrator().HasTable("client_infos") {
|
||||
v0_0_5(db)
|
||||
}
|
||||
// 0.0.5a 修正cors拼写错误
|
||||
if db.Migrator().HasColumn(&conf.V1Struct{}, "allow_cros") {
|
||||
v0_0_5a(db)
|
||||
}
|
||||
// 0.1.4 重建LoadNotification表
|
||||
if db.Migrator().HasColumn(&models.LoadNotification{}, "client") {
|
||||
log.Println("[>0.1.4] Rebuilding LoadNotification table....")
|
||||
db.Migrator().DropTable(&models.LoadNotification{})
|
||||
}
|
||||
// 1.0.2 合并OIDC提供商表
|
||||
if !db.Migrator().HasTable(&models.OidcProvider{}) && db.Migrator().HasTable(&conf.V1Struct{}) {
|
||||
v1_0_2_Oidc(db)
|
||||
}
|
||||
// 1.0.2 迁移消息发送配置到单独的表
|
||||
if !db.Migrator().HasTable(&models.MessageSenderProvider{}) && db.Migrator().HasTable(&conf.V1Struct{}) {
|
||||
v1_0_2_MessageSender(db)
|
||||
}
|
||||
// 1.1.4 迁移配置表
|
||||
if db.Migrator().HasColumn(&conf.V1Struct{}, "id") {
|
||||
v1_1_4(db)
|
||||
}
|
||||
func init() {
|
||||
event.On(eventType.ProcessStart, event.ListenerFunc(func(e event.Event) error {
|
||||
db := dbcore.GetDBInstance()
|
||||
// 0.0.5 迁移ClientInfo
|
||||
if db.Migrator().HasTable("client_infos") {
|
||||
v0_0_5(db)
|
||||
}
|
||||
// 0.0.5a 修正cors拼写错误
|
||||
if db.Migrator().HasColumn(&conf.V1Struct{}, "allow_cros") {
|
||||
v0_0_5a(db)
|
||||
}
|
||||
// 0.1.4 重建LoadNotification表
|
||||
if db.Migrator().HasColumn(&models.LoadNotification{}, "client") {
|
||||
log.Println("[>0.1.4] Rebuilding LoadNotification table....")
|
||||
db.Migrator().DropTable(&models.LoadNotification{})
|
||||
}
|
||||
// 1.0.2 合并OIDC提供商表
|
||||
if !db.Migrator().HasTable(&models.OidcProvider{}) && db.Migrator().HasTable(&conf.V1Struct{}) {
|
||||
v1_0_2_Oidc(db)
|
||||
}
|
||||
// 1.0.2 迁移消息发送配置到单独的表
|
||||
if !db.Migrator().HasTable(&models.MessageSenderProvider{}) && db.Migrator().HasTable(&conf.V1Struct{}) {
|
||||
v1_0_2_MessageSender(db)
|
||||
}
|
||||
// 1.1.4 迁移配置表
|
||||
if db.Migrator().HasColumn(&conf.V1Struct{}, "id") {
|
||||
v1_1_4(db)
|
||||
}
|
||||
return nil
|
||||
}), event.Max)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}))
|
||||
}
|
||||
@@ -1,17 +1,10 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gookit/event"
|
||||
"github.com/komari-monitor/komari/internal"
|
||||
"github.com/komari-monitor/komari/internal/api_rpc"
|
||||
"github.com/komari-monitor/komari/internal/conf"
|
||||
"github.com/komari-monitor/komari/internal/eventType"
|
||||
"github.com/komari-monitor/komari/internal/geoip"
|
||||
"github.com/komari-monitor/komari/internal/messageSender"
|
||||
"github.com/komari-monitor/komari/public"
|
||||
)
|
||||
|
||||
@@ -20,20 +13,11 @@ var (
|
||||
)
|
||||
|
||||
func Init(r *gin.Engine) {
|
||||
config, _ := conf.GetWithV1Format()
|
||||
AllowCors = config.AllowCors
|
||||
|
||||
event.On(eventType.ConfigUpdated, event.ListenerFunc(func(e event.Event) error {
|
||||
newConf := e.Get("new").(conf.Config)
|
||||
oldConf := e.Get("old").(conf.Config)
|
||||
AllowCors = newConf.Site.AllowCors
|
||||
public.UpdateIndex(newConf.ToV1Format())
|
||||
if newConf.GeoIp.GeoIpProvider != oldConf.GeoIp.GeoIpProvider {
|
||||
go geoip.InitGeoIp()
|
||||
}
|
||||
if newConf.Notification.NotificationMethod != oldConf.Notification.NotificationMethod {
|
||||
go messageSender.Initialize()
|
||||
}
|
||||
return nil
|
||||
}), event.High)
|
||||
|
||||
@@ -56,37 +40,4 @@ func Init(r *gin.Engine) {
|
||||
public.Static(r.Group("/"), func(handlers ...gin.HandlerFunc) {
|
||||
r.NoRoute(handlers...)
|
||||
})
|
||||
// #region 静态文件服务
|
||||
public.UpdateIndex(config)
|
||||
|
||||
internal.LoadApiV1Routes(r, config)
|
||||
|
||||
api_rpc.RegisterRouters("/api/rpc2", r)
|
||||
}
|
||||
|
||||
func ScheduledTasksInit() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user