mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 06:47:20 +08:00
feat: 添加操作日志记录功能和前端日志查看页面 (#1227)
* Initial plan * feat: 添加操作日志记录功能和前端日志查看页面 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: 修复 HttpLog 中 IP 字符串处理的类型安全问题 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: lint * feat: 完善面板内日志记录,添加网站/数据库/项目/计划任务/备份CRUD日志 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * feat: 完善更多模块的日志记录(证书/SSH/Webhook/用户/设置/安全) Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: 改进cert_dns删除方法,在删除前获取实体信息用于日志记录 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: lint * feat: 从session获取操作员ID并在前端显示用户名 Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> * fix: lint --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com> Co-authored-by: 耗子 <haozi@loli.email>
This commit is contained in:
@@ -67,30 +67,30 @@ func initWeb() (*app.Web, error) {
|
||||
appRepo := data.NewAppRepo(locale, config, db, logger, cacheRepo, taskRepo)
|
||||
userTokenRepo := data.NewUserTokenRepo(locale, config, db)
|
||||
middlewares := middleware.NewMiddlewares(config, manager, appRepo, userTokenRepo)
|
||||
userRepo := data.NewUserRepo(locale, db)
|
||||
userRepo := data.NewUserRepo(locale, db, logger)
|
||||
userService := service.NewUserService(locale, config, manager, userRepo)
|
||||
userTokenService := service.NewUserTokenService(locale, userTokenRepo)
|
||||
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, logger, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(locale, db, logger, databaseServerRepo, databaseUserRepo)
|
||||
certRepo := data.NewCertRepo(locale, db, logger)
|
||||
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
|
||||
settingRepo := data.NewSettingRepo(locale, db, config, taskRepo)
|
||||
websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo)
|
||||
settingRepo := data.NewSettingRepo(locale, db, logger, config, taskRepo)
|
||||
websiteRepo := data.NewWebsiteRepo(locale, db, logger, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo)
|
||||
environmentRepo := data.NewEnvironmentRepo(locale, config, cacheRepo, taskRepo)
|
||||
cronRepo := data.NewCronRepo(locale, db)
|
||||
backupRepo := data.NewBackupRepo(locale, config, db, settingRepo, websiteRepo)
|
||||
cronRepo := data.NewCronRepo(locale, db, logger)
|
||||
backupRepo := data.NewBackupRepo(locale, config, db, logger, settingRepo, websiteRepo)
|
||||
homeService := service.NewHomeService(locale, config, taskRepo, websiteRepo, appRepo, environmentRepo, settingRepo, cronRepo, backupRepo)
|
||||
taskService := service.NewTaskService(taskRepo)
|
||||
websiteService := service.NewWebsiteService(websiteRepo, settingRepo)
|
||||
projectRepo := data.NewProjectRepo(locale, db)
|
||||
projectRepo := data.NewProjectRepo(locale, db, logger)
|
||||
projectService := service.NewProjectService(projectRepo)
|
||||
databaseService := service.NewDatabaseService(databaseRepo)
|
||||
databaseServerService := service.NewDatabaseServerService(databaseServerRepo)
|
||||
databaseUserService := service.NewDatabaseUserService(databaseUserRepo)
|
||||
backupService := service.NewBackupService(locale, backupRepo)
|
||||
certService := service.NewCertService(locale, certRepo)
|
||||
certDNSRepo := data.NewCertDNSRepo(db)
|
||||
certDNSRepo := data.NewCertDNSRepo(db, logger)
|
||||
certDNSService := service.NewCertDNSService(certDNSRepo)
|
||||
certAccountService := service.NewCertAccountService(certAccountRepo)
|
||||
appService := service.NewAppService(locale, appRepo, cacheRepo, settingRepo)
|
||||
@@ -98,10 +98,10 @@ func initWeb() (*app.Web, error) {
|
||||
environmentPHPService := service.NewEnvironmentPHPService(locale, config, environmentRepo, taskRepo)
|
||||
cronService := service.NewCronService(cronRepo)
|
||||
processService := service.NewProcessService()
|
||||
safeRepo := data.NewSafeRepo()
|
||||
safeRepo := data.NewSafeRepo(logger)
|
||||
safeService := service.NewSafeService(safeRepo)
|
||||
firewallService := service.NewFirewallService()
|
||||
sshRepo := data.NewSSHRepo(locale, db)
|
||||
sshRepo := data.NewSSHRepo(locale, db, logger)
|
||||
sshService := service.NewSSHService(sshRepo)
|
||||
containerRepo := data.NewContainerRepo()
|
||||
containerService := service.NewContainerService(containerRepo)
|
||||
@@ -114,6 +114,8 @@ func initWeb() (*app.Web, error) {
|
||||
containerVolumeRepo := data.NewContainerVolumeRepo()
|
||||
containerVolumeService := service.NewContainerVolumeService(containerVolumeRepo)
|
||||
fileService := service.NewFileService(locale, taskRepo)
|
||||
logRepo := data.NewLogRepo(db)
|
||||
logService := service.NewLogService(logRepo)
|
||||
monitorRepo := data.NewMonitorRepo(db, settingRepo)
|
||||
monitorService := service.NewMonitorService(settingRepo, monitorRepo)
|
||||
settingService := service.NewSettingService(locale, db, settingRepo, certRepo, certAccountRepo)
|
||||
@@ -123,7 +125,7 @@ func initWeb() (*app.Web, error) {
|
||||
toolboxSSHService := service.NewToolboxSSHService(locale)
|
||||
toolboxDiskService := service.NewToolboxDiskService(locale)
|
||||
toolboxLogService := service.NewToolboxLogService(locale, db, containerImageRepo, settingRepo)
|
||||
webHookRepo := data.NewWebHookRepo(locale, db)
|
||||
webHookRepo := data.NewWebHookRepo(locale, db, logger)
|
||||
webHookService := service.NewWebHookService(webHookRepo)
|
||||
codeserverApp := codeserver.NewApp()
|
||||
dockerApp := docker.NewApp()
|
||||
@@ -146,7 +148,7 @@ func initWeb() (*app.Web, error) {
|
||||
s3fsApp := s3fs.NewApp(locale)
|
||||
supervisorApp := supervisor.NewApp(locale)
|
||||
loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, mariadbApp, memcachedApp, minioApp, mysqlApp, nginxApp, openrestyApp, perconaApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
|
||||
http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, projectService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, environmentService, environmentPHPService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, toolboxSSHService, toolboxDiskService, toolboxLogService, webHookService, loader)
|
||||
http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, projectService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, environmentService, environmentPHPService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, logService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, toolboxSSHService, toolboxDiskService, toolboxLogService, webHookService, loader)
|
||||
wsService := service.NewWsService(locale, config, logger, sshRepo)
|
||||
ws := route.NewWs(wsService)
|
||||
mux, err := bootstrap.NewRouter(locale, middlewares, http, ws)
|
||||
|
||||
@@ -59,15 +59,15 @@ func initCli() (*app.Cli, error) {
|
||||
queue := bootstrap.NewQueue()
|
||||
taskRepo := data.NewTaskRepo(locale, db, logger, queue)
|
||||
appRepo := data.NewAppRepo(locale, config, db, logger, cacheRepo, taskRepo)
|
||||
userRepo := data.NewUserRepo(locale, db)
|
||||
settingRepo := data.NewSettingRepo(locale, db, config, taskRepo)
|
||||
userRepo := data.NewUserRepo(locale, db, logger)
|
||||
settingRepo := data.NewSettingRepo(locale, db, logger, config, taskRepo)
|
||||
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, logger, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(locale, db, logger, databaseServerRepo, databaseUserRepo)
|
||||
certRepo := data.NewCertRepo(locale, db, logger)
|
||||
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
|
||||
websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo)
|
||||
backupRepo := data.NewBackupRepo(locale, config, db, settingRepo, websiteRepo)
|
||||
websiteRepo := data.NewWebsiteRepo(locale, db, logger, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo)
|
||||
backupRepo := data.NewBackupRepo(locale, config, db, logger, settingRepo, websiteRepo)
|
||||
cliService := service.NewCliService(locale, config, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo, certRepo, certAccountRepo)
|
||||
cli := route.NewCli(locale, cliService)
|
||||
command := bootstrap.NewCli(locale, cli)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package biz
|
||||
|
||||
import "github.com/acepanel/panel/pkg/types"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/acepanel/panel/pkg/types"
|
||||
)
|
||||
|
||||
type BackupType string
|
||||
|
||||
@@ -15,9 +19,9 @@ const (
|
||||
|
||||
type BackupRepo interface {
|
||||
List(typ BackupType) ([]*types.BackupFile, error)
|
||||
Create(typ BackupType, target string, path ...string) error
|
||||
Delete(typ BackupType, name string) error
|
||||
Restore(typ BackupType, backup, target string) error
|
||||
Create(ctx context.Context, typ BackupType, target string, path ...string) error
|
||||
Delete(ctx context.Context, typ BackupType, name string) error
|
||||
Restore(ctx context.Context, typ BackupType, backup, target string) error
|
||||
ClearExpired(path, prefix string, save int) error
|
||||
CutoffLog(path, target string) error
|
||||
GetPath(typ BackupType) (string, error)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
mholtacme "github.com/mholt/acmez/v3/acme"
|
||||
@@ -35,10 +36,10 @@ type CertRepo interface {
|
||||
List(page, limit uint) ([]*types.CertList, int64, error)
|
||||
Get(id uint) (*Cert, error)
|
||||
GetByWebsite(WebsiteID uint) (*Cert, error)
|
||||
Upload(req *request.CertUpload) (*Cert, error)
|
||||
Create(req *request.CertCreate) (*Cert, error)
|
||||
Update(req *request.CertUpdate) error
|
||||
Delete(id uint) error
|
||||
Upload(ctx context.Context, req *request.CertUpload) (*Cert, error)
|
||||
Create(ctx context.Context, req *request.CertCreate) (*Cert, error)
|
||||
Update(ctx context.Context, req *request.CertUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
ObtainAuto(id uint) (*acme.Certificate, error)
|
||||
ObtainManual(id uint) (*acme.Certificate, error)
|
||||
ObtainPanel(account *CertAccount, ips []string) ([]byte, []byte, error)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
@@ -24,7 +25,7 @@ type CertAccountRepo interface {
|
||||
List(page, limit uint) ([]*CertAccount, int64, error)
|
||||
GetDefault(userID uint) (*CertAccount, error)
|
||||
Get(id uint) (*CertAccount, error)
|
||||
Create(req *request.CertAccountCreate) (*CertAccount, error)
|
||||
Update(req *request.CertAccountUpdate) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, req *request.CertAccountCreate) (*CertAccount, error)
|
||||
Update(ctx context.Context, req *request.CertAccountUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
@@ -21,7 +22,7 @@ type CertDNS struct {
|
||||
type CertDNSRepo interface {
|
||||
List(page, limit uint) ([]*CertDNS, int64, error)
|
||||
Get(id uint) (*CertDNS, error)
|
||||
Create(req *request.CertDNSCreate) (*CertDNS, error)
|
||||
Update(req *request.CertDNSUpdate) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, req *request.CertDNSCreate) (*CertDNS, error)
|
||||
Update(ctx context.Context, req *request.CertDNSUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
@@ -22,8 +23,8 @@ type CronRepo interface {
|
||||
Count() (int64, error)
|
||||
List(page, limit uint) ([]*Cron, int64, error)
|
||||
Get(id uint) (*Cron, error)
|
||||
Create(req *request.CronCreate) error
|
||||
Update(req *request.CronUpdate) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, req *request.CronCreate) error
|
||||
Update(ctx context.Context, req *request.CronUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
Status(id uint, status bool) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
)
|
||||
|
||||
@@ -25,7 +27,7 @@ type Database struct {
|
||||
|
||||
type DatabaseRepo interface {
|
||||
List(page, limit uint) ([]*Database, int64, error)
|
||||
Create(req *request.DatabaseCreate) error
|
||||
Delete(serverID uint, name string) error
|
||||
Create(ctx context.Context, req *request.DatabaseCreate) error
|
||||
Delete(ctx context.Context, serverID uint, name string) error
|
||||
Comment(req *request.DatabaseComment) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/libtnb/utils/crypt"
|
||||
@@ -65,9 +66,9 @@ type DatabaseUserRepo interface {
|
||||
Count() (int64, error)
|
||||
List(page, limit uint) ([]*DatabaseUser, int64, error)
|
||||
Get(id uint) (*DatabaseUser, error)
|
||||
Create(req *request.DatabaseUserCreate) error
|
||||
Create(ctx context.Context, req *request.DatabaseUserCreate) error
|
||||
Update(req *request.DatabaseUserUpdate) error
|
||||
UpdateRemark(req *request.DatabaseUserUpdateRemark) error
|
||||
Delete(id uint) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
DeleteByNames(serverID uint, names []string) error
|
||||
}
|
||||
|
||||
51
internal/biz/log.go
Normal file
51
internal/biz/log.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
LogTypeApp = "app"
|
||||
LogTypeDB = "db"
|
||||
LogTypeHTTP = "http"
|
||||
)
|
||||
|
||||
// 操作日志类型常量
|
||||
const (
|
||||
OperationTypePanel = "panel"
|
||||
OperationTypeWebsite = "website"
|
||||
OperationTypeDatabase = "database"
|
||||
OperationTypeDatabaseUser = "database_user"
|
||||
OperationTypeDatabaseServer = "database_server"
|
||||
OperationTypeProject = "project"
|
||||
OperationTypeCert = "cert"
|
||||
OperationTypeFile = "file"
|
||||
OperationTypeApp = "app"
|
||||
OperationTypeCron = "cron"
|
||||
OperationTypeBackup = "backup"
|
||||
OperationTypeContainer = "container"
|
||||
OperationTypeFirewall = "firewall"
|
||||
OperationTypeSafe = "safe"
|
||||
OperationTypeSSH = "ssh"
|
||||
OperationTypeSetting = "setting"
|
||||
OperationTypeMonitor = "monitor"
|
||||
OperationTypeWebhook = "webhook"
|
||||
OperationTypeUser = "user"
|
||||
)
|
||||
|
||||
// LogEntry 日志条目
|
||||
type LogEntry struct {
|
||||
Time time.Time `json:"time"`
|
||||
Level string `json:"level"`
|
||||
Msg string `json:"msg"`
|
||||
Type string `json:"type,omitempty"`
|
||||
OperatorID uint `json:"operator_id,omitempty"`
|
||||
OperatorName string `json:"operator_name,omitempty"`
|
||||
Extra map[string]any `json:"extra,omitempty"`
|
||||
}
|
||||
|
||||
// LogRepo 日志仓库接口
|
||||
type LogRepo interface {
|
||||
// List 获取日志列表
|
||||
List(logType string, limit int) ([]LogEntry, error)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
@@ -19,7 +20,7 @@ type Project struct {
|
||||
type ProjectRepo interface {
|
||||
List(typ types.ProjectType, page, limit uint) ([]*types.ProjectDetail, int64, error)
|
||||
Get(id uint) (*types.ProjectDetail, error)
|
||||
Create(req *request.ProjectCreate) (*types.ProjectDetail, error)
|
||||
Update(req *request.ProjectUpdate) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, req *request.ProjectCreate) (*types.ProjectDetail, error)
|
||||
Update(ctx context.Context, req *request.ProjectUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package biz
|
||||
|
||||
import "context"
|
||||
|
||||
type SafeRepo interface {
|
||||
GetSSH() (uint, bool, error)
|
||||
UpdateSSH(port uint, status bool) error
|
||||
UpdateSSH(ctx context.Context, port uint, status bool) error
|
||||
GetPingStatus() (bool, error)
|
||||
UpdatePingStatus(status bool) error
|
||||
UpdatePingStatus(ctx context.Context, status bool) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
@@ -44,6 +45,6 @@ type SettingRepo interface {
|
||||
SetSlice(key SettingKey, value []string) error
|
||||
Delete(key SettingKey) error
|
||||
GetPanel() (*request.SettingPanel, error)
|
||||
UpdatePanel(req *request.SettingPanel) (bool, error)
|
||||
UpdatePanel(ctx context.Context, req *request.SettingPanel) (bool, error)
|
||||
UpdateCert(req *request.SettingCert) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/libtnb/utils/crypt"
|
||||
@@ -61,7 +62,7 @@ func (r *SSH) AfterFind(tx *gorm.DB) error {
|
||||
type SSHRepo interface {
|
||||
List(page, limit uint) ([]*SSH, int64, error)
|
||||
Get(id uint) (*SSH, error)
|
||||
Create(req *request.SSHCreate) error
|
||||
Update(req *request.SSHUpdate) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, req *request.SSHCreate) error
|
||||
Update(ctx context.Context, req *request.SSHUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"image"
|
||||
"time"
|
||||
|
||||
@@ -23,11 +24,11 @@ type User struct {
|
||||
type UserRepo interface {
|
||||
List(page, limit uint) ([]*User, int64, error)
|
||||
Get(id uint) (*User, error)
|
||||
Create(username, password, email string) (*User, error)
|
||||
UpdateUsername(id uint, username string) error
|
||||
UpdatePassword(id uint, password string) error
|
||||
UpdateEmail(id uint, email string) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, username, password, email string) (*User, error)
|
||||
UpdateUsername(ctx context.Context, id uint, username string) error
|
||||
UpdatePassword(ctx context.Context, id uint, password string) error
|
||||
UpdateEmail(ctx context.Context, id uint, email string) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
CheckPassword(username, password string) (*User, error)
|
||||
IsTwoFA(username string) (bool, error)
|
||||
GenerateTwoFA(id uint) (image.Image, string, string, error)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
@@ -24,8 +25,8 @@ type WebHookRepo interface {
|
||||
List(page, limit uint) ([]*WebHook, int64, error)
|
||||
Get(id uint) (*WebHook, error)
|
||||
GetByKey(key string) (*WebHook, error)
|
||||
Create(req *request.WebHookCreate) (*WebHook, error)
|
||||
Update(req *request.WebHookUpdate) error
|
||||
Delete(id uint) error
|
||||
Create(ctx context.Context, req *request.WebHookCreate) (*WebHook, error)
|
||||
Update(ctx context.Context, req *request.WebHookUpdate) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
Call(key string) (string, error)
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ type WebsiteRepo interface {
|
||||
Get(id uint) (*types.WebsiteSetting, error)
|
||||
GetByName(name string) (*types.WebsiteSetting, error)
|
||||
List(typ string, page, limit uint) ([]*Website, int64, error)
|
||||
Create(req *request.WebsiteCreate) (*Website, error)
|
||||
Update(req *request.WebsiteUpdate) error
|
||||
Delete(req *request.WebsiteDelete) error
|
||||
Create(ctx context.Context, req *request.WebsiteCreate) (*Website, error)
|
||||
Update(ctx context.Context, req *request.WebsiteUpdate) error
|
||||
Delete(ctx context.Context, req *request.WebsiteDelete) error
|
||||
ClearLog(id uint) error
|
||||
UpdateRemark(id uint, remark string) error
|
||||
ResetConfig(id uint) error
|
||||
|
||||
@@ -217,7 +217,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
|
||||
// 下载回调
|
||||
if err = r.api.AppCallback(slug); err != nil {
|
||||
r.log.Warn("[App] download callback failed", slog.String("app", slug), slog.Any("err", err))
|
||||
r.log.Warn("download callback failed", slog.String("type", biz.OperationTypeApp), slog.Uint64("operator_id", 0), slog.String("app", slug), slog.Any("err", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
@@ -332,7 +332,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
|
||||
// 下载回调
|
||||
if err = r.api.AppCallback(slug); err != nil {
|
||||
r.log.Warn("[App] download callback failed", slog.String("app", slug), slog.Any("err", err))
|
||||
r.log.Warn("download callback failed", slog.String("type", biz.OperationTypeApp), slog.Uint64("operator_id", 0), slog.String("app", slug), slog.Any("err", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@@ -27,15 +29,17 @@ type backupRepo struct {
|
||||
t *gotext.Locale
|
||||
conf *config.Config
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
setting biz.SettingRepo
|
||||
website biz.WebsiteRepo
|
||||
}
|
||||
|
||||
func NewBackupRepo(t *gotext.Locale, conf *config.Config, db *gorm.DB, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo {
|
||||
func NewBackupRepo(t *gotext.Locale, conf *config.Config, db *gorm.DB, log *slog.Logger, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo {
|
||||
return &backupRepo{
|
||||
t: t,
|
||||
conf: conf,
|
||||
db: db,
|
||||
log: log,
|
||||
setting: setting,
|
||||
website: website,
|
||||
}
|
||||
@@ -74,7 +78,7 @@ func (r *backupRepo) List(typ biz.BackupType) ([]*types.BackupFile, error) {
|
||||
// typ 备份类型
|
||||
// target 目标名称
|
||||
// path 可选备份保存路径
|
||||
func (r *backupRepo) Create(typ biz.BackupType, target string, path ...string) error {
|
||||
func (r *backupRepo) Create(ctx context.Context, typ biz.BackupType, target string, path ...string) error {
|
||||
defPath, err := r.GetPath(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -83,37 +87,53 @@ func (r *backupRepo) Create(typ biz.BackupType, target string, path ...string) e
|
||||
defPath = path[0]
|
||||
}
|
||||
|
||||
var createErr error
|
||||
switch typ {
|
||||
case biz.BackupTypeWebsite:
|
||||
return r.createWebsite(defPath, target)
|
||||
createErr = r.createWebsite(defPath, target)
|
||||
case biz.BackupTypeMySQL:
|
||||
return r.createMySQL(defPath, target)
|
||||
createErr = r.createMySQL(defPath, target)
|
||||
case biz.BackupTypePostgres:
|
||||
return r.createPostgres(defPath, target)
|
||||
createErr = r.createPostgres(defPath, target)
|
||||
case biz.BackupTypePanel:
|
||||
return r.createPanel(defPath)
|
||||
|
||||
createErr = r.createPanel(defPath)
|
||||
default:
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
if createErr != nil {
|
||||
return createErr
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("backup created", slog.String("type", biz.OperationTypeBackup), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("backup_type", string(typ)), slog.String("target", target))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除备份
|
||||
func (r *backupRepo) Delete(typ biz.BackupType, name string) error {
|
||||
func (r *backupRepo) Delete(ctx context.Context, typ biz.BackupType, name string) error {
|
||||
path, err := r.GetPath(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file := filepath.Join(path, name)
|
||||
return io.Remove(file)
|
||||
if err = io.Remove(file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("backup deleted", slog.String("type", biz.OperationTypeBackup), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("backup_type", string(typ)), slog.String("name", name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restore 恢复备份
|
||||
// typ 备份类型
|
||||
// backup 备份压缩包,可以是绝对路径或者相对路径
|
||||
// target 目标名称
|
||||
func (r *backupRepo) Restore(typ biz.BackupType, backup, target string) error {
|
||||
func (r *backupRepo) Restore(ctx context.Context, typ biz.BackupType, backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
path, err := r.GetPath(typ)
|
||||
if err != nil {
|
||||
@@ -122,16 +142,26 @@ func (r *backupRepo) Restore(typ biz.BackupType, backup, target string) error {
|
||||
backup = filepath.Join(path, backup)
|
||||
}
|
||||
|
||||
var restoreErr error
|
||||
switch typ {
|
||||
case biz.BackupTypeWebsite:
|
||||
return r.restoreWebsite(backup, target)
|
||||
restoreErr = r.restoreWebsite(backup, target)
|
||||
case biz.BackupTypeMySQL:
|
||||
return r.restoreMySQL(backup, target)
|
||||
restoreErr = r.restoreMySQL(backup, target)
|
||||
case biz.BackupTypePostgres:
|
||||
return r.restorePostgres(backup, target)
|
||||
restoreErr = r.restorePostgres(backup, target)
|
||||
default:
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
if restoreErr != nil {
|
||||
return restoreErr
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("backup restored", slog.String("type", biz.OperationTypeBackup), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("backup_type", string(typ)), slog.String("target", target))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CutoffLog 切割日志
|
||||
@@ -745,7 +775,7 @@ func (r *backupRepo) UpdatePanel(version, url, checksum string) error {
|
||||
fmt.Println(r.t.Get("|-Backup panel data..."))
|
||||
}
|
||||
// 备份面板
|
||||
if err := r.Create(biz.BackupTypePanel, ""); err != nil {
|
||||
if err := r.Create(context.Background(), biz.BackupTypePanel, ""); err != nil {
|
||||
return errors.New(r.t.Get("|-Backup panel data failed: %v", err))
|
||||
}
|
||||
if err := io.Compress(filepath.Join(app.Root, "panel/storage"), nil, "/tmp/panel-storage.zip"); err != nil {
|
||||
|
||||
@@ -89,7 +89,7 @@ func (r *certRepo) GetByWebsite(WebsiteID uint) (*biz.Cert, error) {
|
||||
return cert, err
|
||||
}
|
||||
|
||||
func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) {
|
||||
func (r *certRepo) Upload(ctx context.Context, req *request.CertUpload) (*biz.Cert, error) {
|
||||
info, err := pkgcert.ParseCert(req.Cert)
|
||||
if err != nil {
|
||||
return nil, errors.New(r.t.Get("failed to parse certificate: %v", err))
|
||||
@@ -108,10 +108,13 @@ func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert uploaded", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(cert.ID)))
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
func (r *certRepo) Create(req *request.CertCreate) (*biz.Cert, error) {
|
||||
func (r *certRepo) Create(ctx context.Context, req *request.CertCreate) (*biz.Cert, error) {
|
||||
cert := &biz.Cert{
|
||||
AccountID: req.AccountID,
|
||||
WebsiteID: req.WebsiteID,
|
||||
@@ -123,10 +126,14 @@ func (r *certRepo) Create(req *request.CertCreate) (*biz.Cert, error) {
|
||||
if err := r.db.Create(cert).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert created", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(cert.ID)), slog.String("cert_type", req.Type))
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
func (r *certRepo) Update(ctx context.Context, req *request.CertUpdate) error {
|
||||
info, err := pkgcert.ParseCert(req.Cert)
|
||||
if err == nil && req.Type == "upload" {
|
||||
req.Domains = info.DNSNames
|
||||
@@ -135,7 +142,7 @@ func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
return errors.New(r.t.Get("upload certificate cannot be set to auto renewal"))
|
||||
}
|
||||
|
||||
return r.db.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{
|
||||
if err = r.db.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{
|
||||
ID: req.ID,
|
||||
AccountID: req.AccountID,
|
||||
WebsiteID: req.WebsiteID,
|
||||
@@ -146,11 +153,25 @@ func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
Script: req.Script,
|
||||
Domains: req.Domains,
|
||||
AutoRenewal: req.AutoRenewal,
|
||||
}).Error
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert updated", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *certRepo) Delete(id uint) error {
|
||||
return r.db.Model(&biz.Cert{}).Where("id = ?", id).Delete(&biz.Cert{}).Error
|
||||
func (r *certRepo) Delete(ctx context.Context, id uint) error {
|
||||
if err := r.db.Model(&biz.Cert{}).Where("id = ?", id).Delete(&biz.Cert{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert deleted", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *certRepo) ObtainAuto(id uint) (*acme.Certificate, error) {
|
||||
|
||||
@@ -56,7 +56,7 @@ func (r certAccountRepo) GetDefault(userID uint) (*biz.CertAccount, error) {
|
||||
KeyType: string(acme.KeyEC256),
|
||||
}
|
||||
|
||||
return r.Create(req)
|
||||
return r.Create(context.Background(), req)
|
||||
}
|
||||
|
||||
func (r certAccountRepo) Get(id uint) (*biz.CertAccount, error) {
|
||||
@@ -65,7 +65,7 @@ func (r certAccountRepo) Get(id uint) (*biz.CertAccount, error) {
|
||||
return account, err
|
||||
}
|
||||
|
||||
func (r certAccountRepo) Create(req *request.CertAccountCreate) (*biz.CertAccount, error) {
|
||||
func (r certAccountRepo) Create(ctx context.Context, req *request.CertAccountCreate) (*biz.CertAccount, error) {
|
||||
account := new(biz.CertAccount)
|
||||
account.CA = req.CA
|
||||
account.Email = req.Email
|
||||
@@ -118,10 +118,13 @@ func (r certAccountRepo) Create(req *request.CertAccountCreate) (*biz.CertAccoun
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert account created", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(account.ID)), slog.String("ca", req.CA), slog.String("email", req.Email))
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (r certAccountRepo) Update(req *request.CertAccountUpdate) error {
|
||||
func (r certAccountRepo) Update(ctx context.Context, req *request.CertAccountUpdate) error {
|
||||
account, err := r.Get(req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -173,11 +176,25 @@ func (r certAccountRepo) Update(req *request.CertAccountUpdate) error {
|
||||
}
|
||||
account.PrivateKey = string(privateKey)
|
||||
|
||||
return r.db.Save(account).Error
|
||||
if err = r.db.Save(account).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert account updated", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("ca", req.CA))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r certAccountRepo) Delete(id uint) error {
|
||||
return r.db.Model(&biz.CertAccount{}).Where("id = ?", id).Delete(&biz.CertAccount{}).Error
|
||||
func (r certAccountRepo) Delete(ctx context.Context, id uint) error {
|
||||
if err := r.db.Model(&biz.CertAccount{}).Where("id = ?", id).Delete(&biz.CertAccount{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert account deleted", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getGoogleEAB 获取 Google EAB
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
@@ -8,12 +11,14 @@ import (
|
||||
)
|
||||
|
||||
type certDNSRepo struct {
|
||||
db *gorm.DB
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewCertDNSRepo(db *gorm.DB) biz.CertDNSRepo {
|
||||
func NewCertDNSRepo(db *gorm.DB, log *slog.Logger) biz.CertDNSRepo {
|
||||
return &certDNSRepo{
|
||||
db: db,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +35,7 @@ func (r certDNSRepo) Get(id uint) (*biz.CertDNS, error) {
|
||||
return certDNS, err
|
||||
}
|
||||
|
||||
func (r certDNSRepo) Create(req *request.CertDNSCreate) (*biz.CertDNS, error) {
|
||||
func (r certDNSRepo) Create(ctx context.Context, req *request.CertDNSCreate) (*biz.CertDNS, error) {
|
||||
certDNS := &biz.CertDNS{
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
@@ -41,10 +46,13 @@ func (r certDNSRepo) Create(req *request.CertDNSCreate) (*biz.CertDNS, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert dns created", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(certDNS.ID)), slog.String("name", req.Name))
|
||||
|
||||
return certDNS, nil
|
||||
}
|
||||
|
||||
func (r certDNSRepo) Update(req *request.CertDNSUpdate) error {
|
||||
func (r certDNSRepo) Update(ctx context.Context, req *request.CertDNSUpdate) error {
|
||||
cert, err := r.Get(req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -54,9 +62,28 @@ func (r certDNSRepo) Update(req *request.CertDNSUpdate) error {
|
||||
cert.Type = req.Type
|
||||
cert.Data = req.Data
|
||||
|
||||
return r.db.Save(cert).Error
|
||||
if err = r.db.Save(cert).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert dns updated", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", req.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r certDNSRepo) Delete(id uint) error {
|
||||
return r.db.Model(&biz.CertDNS{}).Where("id = ?", id).Delete(&biz.CertDNS{}).Error
|
||||
func (r certDNSRepo) Delete(ctx context.Context, id uint) error {
|
||||
certDNS, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.db.Model(&biz.CertDNS{}).Where("id = ?", id).Delete(&biz.CertDNS{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cert dns deleted", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("name", certDNS.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
@@ -19,14 +21,16 @@ import (
|
||||
)
|
||||
|
||||
type cronRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewCronRepo(t *gotext.Locale, db *gorm.DB) biz.CronRepo {
|
||||
func NewCronRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.CronRepo {
|
||||
return &cronRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +59,7 @@ func (r *cronRepo) Get(id uint) (*biz.Cron, error) {
|
||||
return cron, nil
|
||||
}
|
||||
|
||||
func (r *cronRepo) Create(req *request.CronCreate) error {
|
||||
func (r *cronRepo) Create(ctx context.Context, req *request.CronCreate) error {
|
||||
var script string
|
||||
if req.Type == "backup" {
|
||||
if req.BackupType == "website" {
|
||||
@@ -111,10 +115,13 @@ acepanel cutoff clear -t website -f '%s' -s '%d' -p '%s'
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cron created", slog.String("type", biz.OperationTypeCron), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", req.Name), slog.String("cron_type", req.Type))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *cronRepo) Update(req *request.CronUpdate) error {
|
||||
func (r *cronRepo) Update(ctx context.Context, req *request.CronUpdate) error {
|
||||
cron, err := r.Get(req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -142,10 +149,13 @@ func (r *cronRepo) Update(req *request.CronUpdate) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cron updated", slog.String("type", biz.OperationTypeCron), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", cron.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *cronRepo) Delete(id uint) error {
|
||||
func (r *cronRepo) Delete(ctx context.Context, id uint) error {
|
||||
cron, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -158,7 +168,14 @@ func (r *cronRepo) Delete(id uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.db.Delete(cron).Error
|
||||
if err = r.db.Delete(cron).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("cron deleted", slog.String("type", biz.OperationTypeCron), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("name", cron.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *cronRepo) Status(id uint, status bool) error {
|
||||
|
||||
@@ -20,6 +20,7 @@ var ProviderSet = wire.NewSet(
|
||||
NewDatabaseServerRepo,
|
||||
NewDatabaseUserRepo,
|
||||
NewEnvironmentRepo,
|
||||
NewLogRepo,
|
||||
NewMonitorRepo,
|
||||
NewProjectRepo,
|
||||
NewSafeRepo,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
@@ -16,14 +18,16 @@ import (
|
||||
type databaseRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
server biz.DatabaseServerRepo
|
||||
user biz.DatabaseUserRepo
|
||||
}
|
||||
|
||||
func NewDatabaseRepo(t *gotext.Locale, db *gorm.DB, server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo {
|
||||
func NewDatabaseRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger, server biz.DatabaseServerRepo, user biz.DatabaseUserRepo) biz.DatabaseRepo {
|
||||
return &databaseRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
server: server,
|
||||
user: user,
|
||||
}
|
||||
@@ -65,7 +69,7 @@ func (r *databaseRepo) List(page, limit uint) ([]*biz.Database, int64, error) {
|
||||
return database[(page-1)*limit:], int64(len(database)), nil
|
||||
}
|
||||
|
||||
func (r *databaseRepo) Create(req *request.DatabaseCreate) error {
|
||||
func (r *databaseRepo) Create(ctx context.Context, req *request.DatabaseCreate) error {
|
||||
server, err := r.server.Get(req.ServerID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -80,7 +84,7 @@ func (r *databaseRepo) Create(req *request.DatabaseCreate) error {
|
||||
switch server.Type {
|
||||
case biz.DatabaseTypeMysql:
|
||||
if req.CreateUser {
|
||||
if err = r.user.Create(&request.DatabaseUserCreate{
|
||||
if err = r.user.Create(ctx, &request.DatabaseUserCreate{
|
||||
ServerID: req.ServerID,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
@@ -99,7 +103,7 @@ func (r *databaseRepo) Create(req *request.DatabaseCreate) error {
|
||||
}
|
||||
case biz.DatabaseTypePostgresql:
|
||||
if req.CreateUser {
|
||||
if err = r.user.Create(&request.DatabaseUserCreate{
|
||||
if err = r.user.Create(ctx, &request.DatabaseUserCreate{
|
||||
ServerID: req.ServerID,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
@@ -121,10 +125,13 @@ func (r *databaseRepo) Create(req *request.DatabaseCreate) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("database created", slog.String("type", biz.OperationTypeDatabase), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", req.Name), slog.Uint64("server_id", uint64(req.ServerID)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *databaseRepo) Delete(serverID uint, name string) error {
|
||||
func (r *databaseRepo) Delete(ctx context.Context, serverID uint, name string) error {
|
||||
server, err := r.server.Get(serverID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -136,7 +143,14 @@ func (r *databaseRepo) Delete(serverID uint, name string) error {
|
||||
}
|
||||
defer operator.Close()
|
||||
|
||||
return operator.DatabaseDrop(name)
|
||||
if err = operator.DatabaseDrop(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("database deleted", slog.String("type", biz.OperationTypeDatabase), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", name), slog.Uint64("server_id", uint64(serverID)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *databaseRepo) Comment(req *request.DatabaseComment) error {
|
||||
|
||||
@@ -160,7 +160,7 @@ func (r *databaseServerRepo) Sync(id uint) error {
|
||||
Remark: r.t.Get("sync from server %s", server.Name),
|
||||
}
|
||||
if err = r.db.Create(newUser).Error; err != nil {
|
||||
r.log.Warn("[DatabaseServer] sync mysql database user failed", slog.Any("err", err))
|
||||
r.log.Warn("sync mysql database user failed", slog.String("type", biz.OperationTypeDatabaseServer), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func (r *databaseServerRepo) Sync(id uint) error {
|
||||
Remark: r.t.Get("sync from server %s", server.Name),
|
||||
}
|
||||
if err = r.db.Create(newUser).Error; err != nil {
|
||||
r.log.Warn("[DatabaseServer] sync postgresql database user failed", slog.Any("err", err))
|
||||
r.log.Warn("sync postgresql database user failed", slog.String("type", biz.OperationTypeDatabaseServer), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
@@ -15,13 +17,15 @@ import (
|
||||
type databaseUserRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
server biz.DatabaseServerRepo
|
||||
}
|
||||
|
||||
func NewDatabaseUserRepo(t *gotext.Locale, db *gorm.DB, server biz.DatabaseServerRepo) biz.DatabaseUserRepo {
|
||||
func NewDatabaseUserRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger, server biz.DatabaseServerRepo) biz.DatabaseUserRepo {
|
||||
return &databaseUserRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
server: server,
|
||||
}
|
||||
}
|
||||
@@ -58,7 +62,7 @@ func (r *databaseUserRepo) Get(id uint) (*biz.DatabaseUser, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *databaseUserRepo) Create(req *request.DatabaseUserCreate) error {
|
||||
func (r *databaseUserRepo) Create(ctx context.Context, req *request.DatabaseUserCreate) error {
|
||||
server, err := r.server.Get(req.ServerID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -97,7 +101,14 @@ func (r *databaseUserRepo) Create(req *request.DatabaseUserCreate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.db.Save(user).Error
|
||||
if err = r.db.Save(user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("database user created", slog.String("type", biz.OperationTypeDatabaseUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("username", req.Username), slog.Uint64("server_id", uint64(req.ServerID)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *databaseUserRepo) Update(req *request.DatabaseUserUpdate) error {
|
||||
@@ -151,7 +162,7 @@ func (r *databaseUserRepo) UpdateRemark(req *request.DatabaseUserUpdateRemark) e
|
||||
return r.db.Save(user).Error
|
||||
}
|
||||
|
||||
func (r *databaseUserRepo) Delete(id uint) error {
|
||||
func (r *databaseUserRepo) Delete(ctx context.Context, id uint) error {
|
||||
user, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -170,7 +181,14 @@ func (r *databaseUserRepo) Delete(id uint) error {
|
||||
|
||||
_ = operator.UserDrop(user.Username, user.Host)
|
||||
|
||||
return r.db.Where("id = ?", id).Delete(&biz.DatabaseUser{}).Error
|
||||
if err = r.db.Where("id = ?", id).Delete(&biz.DatabaseUser{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("database user deleted", slog.String("type", biz.OperationTypeDatabaseUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("username", user.Username))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *databaseUserRepo) DeleteByNames(serverID uint, names []string) error {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
func getDockerClient(sock string) (*client.Client, error) {
|
||||
@@ -14,3 +16,16 @@ func getDockerClient(sock string) (*client.Client, error) {
|
||||
|
||||
return apiClient, nil
|
||||
}
|
||||
|
||||
// getOperatorID 从 context 中获取操作员ID
|
||||
// 如果无法获取,返回 0(表示系统操作)
|
||||
func getOperatorID(ctx context.Context) uint64 {
|
||||
if ctx == nil {
|
||||
return 0
|
||||
}
|
||||
userID := ctx.Value("user_id")
|
||||
if userID == nil {
|
||||
return 0
|
||||
}
|
||||
return cast.ToUint64(userID)
|
||||
}
|
||||
|
||||
179
internal/data/log.go
Normal file
179
internal/data/log.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/acepanel/panel/internal/app"
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
)
|
||||
|
||||
type logRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewLogRepo(db *gorm.DB) biz.LogRepo {
|
||||
return &logRepo{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// List 获取日志列表
|
||||
func (r *logRepo) List(logType string, limit int) ([]biz.LogEntry, error) {
|
||||
var filename string
|
||||
switch logType {
|
||||
case biz.LogTypeApp:
|
||||
filename = "app.log"
|
||||
case biz.LogTypeDB:
|
||||
filename = "db.log"
|
||||
case biz.LogTypeHTTP:
|
||||
filename = "http.log"
|
||||
default:
|
||||
filename = "app.log"
|
||||
}
|
||||
|
||||
logPath := filepath.Join(app.Root, "panel/storage/logs", filename)
|
||||
|
||||
file, err := os.Open(logPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []biz.LogEntry{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer func(file *os.File) { _ = file.Close() }(file)
|
||||
|
||||
// 读取所有行
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
// 增加缓冲区大小以处理较长的日志行
|
||||
buf := make([]byte, 0, 64*1024)
|
||||
scanner.Buffer(buf, 1024*1024)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.TrimSpace(line) != "" {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 从末尾取指定数量的行
|
||||
start := 0
|
||||
if len(lines) > limit {
|
||||
start = len(lines) - limit
|
||||
}
|
||||
lines = lines[start:]
|
||||
|
||||
// 倒序处理,最新的在前面
|
||||
entries := make([]biz.LogEntry, 0, len(lines))
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
entry, err := r.parseLine(lines[i], logType)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
// 如果是app日志,查询用户名
|
||||
if logType == biz.LogTypeApp {
|
||||
r.fillOperatorNames(entries)
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// fillOperatorNames 填充操作员用户名
|
||||
func (r *logRepo) fillOperatorNames(entries []biz.LogEntry) {
|
||||
// 收集所有用户ID
|
||||
userIDs := make(map[uint]bool)
|
||||
for _, entry := range entries {
|
||||
if entry.OperatorID > 0 {
|
||||
userIDs[entry.OperatorID] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(userIDs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量查询用户名
|
||||
ids := make([]uint, 0, len(userIDs))
|
||||
for id := range userIDs {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
var users []biz.User
|
||||
r.db.Select("id", "username").Where("id IN ?", ids).Find(&users)
|
||||
|
||||
// 构建ID到用户名的映射
|
||||
userMap := make(map[uint]string)
|
||||
for _, user := range users {
|
||||
userMap[user.ID] = user.Username
|
||||
}
|
||||
|
||||
// 填充用户名
|
||||
for i := range entries {
|
||||
if entries[i].OperatorID > 0 {
|
||||
if username, ok := userMap[entries[i].OperatorID]; ok {
|
||||
entries[i].OperatorName = username
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseLine 解析日志行
|
||||
func (r *logRepo) parseLine(line string, logType string) (biz.LogEntry, error) {
|
||||
var rawEntry map[string]any
|
||||
if err := json.Unmarshal([]byte(line), &rawEntry); err != nil {
|
||||
return biz.LogEntry{}, err
|
||||
}
|
||||
|
||||
entry := biz.LogEntry{
|
||||
Extra: make(map[string]any),
|
||||
}
|
||||
|
||||
// 解析通用字段
|
||||
if t, ok := rawEntry["time"].(string); ok {
|
||||
if parsed, err := time.Parse(time.RFC3339Nano, t); err == nil {
|
||||
entry.Time = parsed
|
||||
}
|
||||
}
|
||||
if level, ok := rawEntry["level"].(string); ok {
|
||||
entry.Level = level
|
||||
}
|
||||
if msg, ok := rawEntry["msg"].(string); ok {
|
||||
entry.Msg = msg
|
||||
}
|
||||
|
||||
// 解析操作日志特有字段
|
||||
if logType == biz.LogTypeApp {
|
||||
if t, ok := rawEntry["type"].(string); ok {
|
||||
entry.Type = t
|
||||
}
|
||||
if opID, ok := rawEntry["operator_id"].(float64); ok {
|
||||
entry.OperatorID = uint(opID)
|
||||
}
|
||||
}
|
||||
|
||||
// 其他字段放入Extra
|
||||
excludeKeys := map[string]bool{
|
||||
"time": true, "level": true, "msg": true, "type": true, "operator_id": true,
|
||||
}
|
||||
for k, v := range rawEntry {
|
||||
if !excludeKeys[k] {
|
||||
entry.Extra[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -21,14 +23,16 @@ import (
|
||||
)
|
||||
|
||||
type projectRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewProjectRepo(t *gotext.Locale, db *gorm.DB) biz.ProjectRepo {
|
||||
func NewProjectRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.ProjectRepo {
|
||||
return &projectRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ func (r *projectRepo) Get(id uint) (*types.ProjectDetail, error) {
|
||||
return r.parseProjectDetail(project)
|
||||
}
|
||||
|
||||
func (r *projectRepo) Create(req *request.ProjectCreate) (*types.ProjectDetail, error) {
|
||||
func (r *projectRepo) Create(ctx context.Context, req *request.ProjectCreate) (*types.ProjectDetail, error) {
|
||||
// 检查项目名是否已存在
|
||||
var count int64
|
||||
if err := r.db.Model(&biz.Project{}).Where("name = ?", req.Name).Count(&count).Error; err != nil {
|
||||
@@ -106,10 +110,13 @@ func (r *projectRepo) Create(req *request.ProjectCreate) (*types.ProjectDetail,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("project created", slog.String("type", biz.OperationTypeProject), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", req.Name), slog.String("project_type", string(req.Type)))
|
||||
|
||||
return r.parseProjectDetail(project)
|
||||
}
|
||||
|
||||
func (r *projectRepo) Update(req *request.ProjectUpdate) error {
|
||||
func (r *projectRepo) Update(ctx context.Context, req *request.ProjectUpdate) error {
|
||||
project := new(biz.Project)
|
||||
if err := r.db.First(project, req.ID).Error; err != nil {
|
||||
return err
|
||||
@@ -130,11 +137,14 @@ func (r *projectRepo) Update(req *request.ProjectUpdate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("project updated", slog.String("type", biz.OperationTypeProject), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", project.Name))
|
||||
|
||||
// 更新 systemd unit 文件
|
||||
return r.updateUnitFile(project.Name, req)
|
||||
}
|
||||
|
||||
func (r *projectRepo) Delete(id uint) error {
|
||||
func (r *projectRepo) Delete(ctx context.Context, id uint) error {
|
||||
project := new(biz.Project)
|
||||
if err := r.db.First(project, id).Error; err != nil {
|
||||
return err
|
||||
@@ -146,7 +156,14 @@ func (r *projectRepo) Delete(id uint) error {
|
||||
return fmt.Errorf("%s: %w", r.t.Get("failed to delete systemd config"), err)
|
||||
}
|
||||
|
||||
return r.db.Delete(project).Error
|
||||
if err := r.db.Delete(project).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("project deleted", slog.String("type", biz.OperationTypeProject), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("name", project.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// unitFilePath 返回 systemd unit 文件路径
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@@ -15,9 +17,10 @@ import (
|
||||
|
||||
type safeRepo struct {
|
||||
ssh string
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewSafeRepo() biz.SafeRepo {
|
||||
func NewSafeRepo(log *slog.Logger) biz.SafeRepo {
|
||||
var ssh string
|
||||
if os.IsRHEL() {
|
||||
ssh = "sshd"
|
||||
@@ -26,6 +29,7 @@ func NewSafeRepo() biz.SafeRepo {
|
||||
}
|
||||
return &safeRepo{
|
||||
ssh: ssh,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +47,7 @@ func (r *safeRepo) GetSSH() (uint, bool, error) {
|
||||
return cast.ToUint(out), running, nil
|
||||
}
|
||||
|
||||
func (r *safeRepo) UpdateSSH(port uint, status bool) error {
|
||||
func (r *safeRepo) UpdateSSH(ctx context.Context, port uint, status bool) error {
|
||||
oldPort, err := shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -53,10 +57,19 @@ func (r *safeRepo) UpdateSSH(port uint, status bool) error {
|
||||
_, _ = shell.Execf("sed -i 's/Port %s/Port %d/g' /etc/ssh/sshd_config", oldPort, port)
|
||||
|
||||
if !status {
|
||||
return systemctl.Stop(r.ssh)
|
||||
if err = systemctl.Stop(r.ssh); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = systemctl.Restart(r.ssh); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return systemctl.Restart(r.ssh)
|
||||
// 记录日志
|
||||
r.log.Info("ssh settings updated", slog.String("type", biz.OperationTypeSafe), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("port", uint64(port)), slog.Bool("status", status))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *safeRepo) GetPingStatus() (bool, error) {
|
||||
@@ -72,7 +85,7 @@ func (r *safeRepo) GetPingStatus() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *safeRepo) UpdatePingStatus(status bool) error {
|
||||
func (r *safeRepo) UpdatePingStatus(ctx context.Context, status bool) error {
|
||||
fw, err := firewall.NewFirewall().Status()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -95,5 +108,8 @@ func (r *safeRepo) UpdatePingStatus(status bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("ping status updated", slog.String("type", biz.OperationTypeSafe), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Bool("status", status))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
@@ -25,14 +27,16 @@ type settingRepo struct {
|
||||
t *gotext.Locale
|
||||
cache sync.Map
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
conf *config.Config
|
||||
task biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewSettingRepo(t *gotext.Locale, db *gorm.DB, conf *config.Config, task biz.TaskRepo) biz.SettingRepo {
|
||||
func NewSettingRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger, conf *config.Config, task biz.TaskRepo) biz.SettingRepo {
|
||||
return &settingRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
conf: conf,
|
||||
task: task,
|
||||
}
|
||||
@@ -268,7 +272,7 @@ func (r *settingRepo) GetPanel() (*request.SettingPanel, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) {
|
||||
func (r *settingRepo) UpdatePanel(ctx context.Context, req *request.SettingPanel) (bool, error) {
|
||||
if err := r.Set(biz.SettingKeyName, req.Name); err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -371,6 +375,9 @@ func (r *settingRepo) UpdatePanel(req *request.SettingPanel) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("panel settings updated", slog.String("type", biz.OperationTypeSetting), slog.Uint64("operator_id", getOperatorID(ctx)))
|
||||
|
||||
return restartFlag, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
cryptossh "golang.org/x/crypto/ssh"
|
||||
@@ -14,14 +16,16 @@ import (
|
||||
)
|
||||
|
||||
type sshRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewSSHRepo(t *gotext.Locale, db *gorm.DB) biz.SSHRepo {
|
||||
func NewSSHRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.SSHRepo {
|
||||
return &sshRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +45,7 @@ func (r *sshRepo) Get(id uint) (*biz.SSH, error) {
|
||||
return ssh, nil
|
||||
}
|
||||
|
||||
func (r *sshRepo) Create(req *request.SSHCreate) error {
|
||||
func (r *sshRepo) Create(ctx context.Context, req *request.SSHCreate) error {
|
||||
conf := pkgssh.ClientConfig{
|
||||
AuthMethod: pkgssh.AuthMethod(req.AuthMethod),
|
||||
Host: fmt.Sprintf("%s:%d", req.Host, req.Port),
|
||||
@@ -64,10 +68,17 @@ func (r *sshRepo) Create(req *request.SSHCreate) error {
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
return r.db.Create(ssh).Error
|
||||
if err = r.db.Create(ssh).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("ssh created", slog.String("type", biz.OperationTypeSSH), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", req.Name), slog.String("host", req.Host))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *sshRepo) Update(req *request.SSHUpdate) error {
|
||||
func (r *sshRepo) Update(ctx context.Context, req *request.SSHUpdate) error {
|
||||
conf := pkgssh.ClientConfig{
|
||||
AuthMethod: pkgssh.AuthMethod(req.AuthMethod),
|
||||
Host: fmt.Sprintf("%s:%d", req.Host, req.Port),
|
||||
@@ -91,9 +102,28 @@ func (r *sshRepo) Update(req *request.SSHUpdate) error {
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
return r.db.Model(ssh).Where("id = ?", req.ID).Select("*").Updates(ssh).Error
|
||||
if err = r.db.Model(ssh).Where("id = ?", req.ID).Select("*").Updates(ssh).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("ssh updated", slog.String("type", biz.OperationTypeSSH), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", req.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *sshRepo) Delete(id uint) error {
|
||||
return r.db.Delete(&biz.SSH{}, id).Error
|
||||
func (r *sshRepo) Delete(ctx context.Context, id uint) error {
|
||||
ssh, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = r.db.Delete(&biz.SSH{}, id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("ssh deleted", slog.String("type", biz.OperationTypeSSH), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("name", ssh.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"image"
|
||||
"log/slog"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/utils/hash"
|
||||
@@ -17,13 +19,15 @@ import (
|
||||
type userRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
hasher hash.Hasher
|
||||
}
|
||||
|
||||
func NewUserRepo(t *gotext.Locale, db *gorm.DB) biz.UserRepo {
|
||||
func NewUserRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.UserRepo {
|
||||
return &userRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
hasher: hash.NewArgon2id(),
|
||||
}
|
||||
}
|
||||
@@ -44,7 +48,7 @@ func (r *userRepo) Get(id uint) (*biz.User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *userRepo) Create(username, password, email string) (*biz.User, error) {
|
||||
func (r *userRepo) Create(ctx context.Context, username, password, email string) (*biz.User, error) {
|
||||
value, err := r.hasher.Make(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -59,20 +63,30 @@ func (r *userRepo) Create(username, password, email string) (*biz.User, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("user created", slog.String("type", biz.OperationTypeUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(user.ID)), slog.String("username", username))
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateUsername(id uint, username string) error {
|
||||
func (r *userRepo) UpdateUsername(ctx context.Context, id uint, username string) error {
|
||||
user, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Username = username
|
||||
return r.db.Save(user).Error
|
||||
if err = r.db.Save(user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("user username updated", slog.String("type", biz.OperationTypeUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("username", username))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdatePassword(id uint, password string) error {
|
||||
func (r *userRepo) UpdatePassword(ctx context.Context, id uint, password string) error {
|
||||
value, err := r.hasher.Make(password)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -84,20 +98,34 @@ func (r *userRepo) UpdatePassword(id uint, password string) error {
|
||||
}
|
||||
|
||||
user.Password = value
|
||||
return r.db.Save(user).Error
|
||||
if err = r.db.Save(user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("user password updated", slog.String("type", biz.OperationTypeUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateEmail(id uint, email string) error {
|
||||
func (r *userRepo) UpdateEmail(ctx context.Context, id uint, email string) error {
|
||||
user, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Email = email
|
||||
return r.db.Save(user).Error
|
||||
if err = r.db.Save(user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("user email updated", slog.String("type", biz.OperationTypeUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("email", email))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepo) Delete(id uint) error {
|
||||
func (r *userRepo) Delete(ctx context.Context, id uint) error {
|
||||
var count int64
|
||||
if err := r.db.Model(&biz.User{}).Count(&count).Error; err != nil {
|
||||
return err
|
||||
@@ -111,12 +139,20 @@ func (r *userRepo) Delete(id uint) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
username := user.Username
|
||||
if err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&user).Association("Tokens").Delete(); err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Delete(&user).Error
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("user deleted", slog.String("type", biz.OperationTypeUser), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("username", username))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *userRepo) CheckPassword(username, password string) (*biz.User, error) {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -19,14 +21,16 @@ import (
|
||||
)
|
||||
|
||||
type webhookRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewWebHookRepo(t *gotext.Locale, db *gorm.DB) biz.WebHookRepo {
|
||||
func NewWebHookRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.WebHookRepo {
|
||||
return &webhookRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +57,7 @@ func (r *webhookRepo) GetByKey(key string) (*biz.WebHook, error) {
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
func (r *webhookRepo) Create(req *request.WebHookCreate) (*biz.WebHook, error) {
|
||||
func (r *webhookRepo) Create(ctx context.Context, req *request.WebHookCreate) (*biz.WebHook, error) {
|
||||
if err := os.MkdirAll(r.webhookDir(), 0755); err != nil {
|
||||
return nil, errors.New(r.t.Get("failed to create webhook directory: %v", err))
|
||||
}
|
||||
@@ -78,10 +82,13 @@ func (r *webhookRepo) Create(req *request.WebHookCreate) (*biz.WebHook, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("webhook created", slog.String("type", biz.OperationTypeWebhook), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", req.Name))
|
||||
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
func (r *webhookRepo) Update(req *request.WebHookUpdate) error {
|
||||
func (r *webhookRepo) Update(ctx context.Context, req *request.WebHookUpdate) error {
|
||||
webhook, err := r.Get(req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -92,16 +99,23 @@ func (r *webhookRepo) Update(req *request.WebHookUpdate) error {
|
||||
return errors.New(r.t.Get("failed to write webhook script: %v", err))
|
||||
}
|
||||
|
||||
return r.db.Model(&biz.WebHook{}).Where("id = ?", req.ID).Updates(map[string]any{
|
||||
if err = r.db.Model(&biz.WebHook{}).Where("id = ?", req.ID).Updates(map[string]any{
|
||||
"name": req.Name,
|
||||
"script": req.Script,
|
||||
"raw": req.Raw,
|
||||
"user": req.User,
|
||||
"status": req.Status,
|
||||
}).Error
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("webhook updated", slog.String("type", biz.OperationTypeWebhook), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", req.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *webhookRepo) Delete(id uint) error {
|
||||
func (r *webhookRepo) Delete(ctx context.Context, id uint) error {
|
||||
webhook, err := r.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -110,7 +124,14 @@ func (r *webhookRepo) Delete(id uint) error {
|
||||
scriptFile := r.scriptPath(webhook.Key)
|
||||
_ = os.Remove(scriptFile)
|
||||
|
||||
return r.db.Delete(&biz.WebHook{}, id).Error
|
||||
if err = r.db.Delete(&biz.WebHook{}, id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("webhook deleted", slog.String("type", biz.OperationTypeWebhook), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(id)), slog.String("name", webhook.Name))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *webhookRepo) Call(key string) (string, error) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@@ -35,6 +36,7 @@ import (
|
||||
type websiteRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
cache biz.CacheRepo
|
||||
database biz.DatabaseRepo
|
||||
databaseServer biz.DatabaseServerRepo
|
||||
@@ -44,10 +46,11 @@ type websiteRepo struct {
|
||||
setting biz.SettingRepo
|
||||
}
|
||||
|
||||
func NewWebsiteRepo(t *gotext.Locale, db *gorm.DB, cache biz.CacheRepo, database biz.DatabaseRepo, databaseServer biz.DatabaseServerRepo, databaseUser biz.DatabaseUserRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo, setting biz.SettingRepo) biz.WebsiteRepo {
|
||||
func NewWebsiteRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger, cache biz.CacheRepo, database biz.DatabaseRepo, databaseServer biz.DatabaseServerRepo, databaseUser biz.DatabaseUserRepo, cert biz.CertRepo, certAccount biz.CertAccountRepo, setting biz.SettingRepo) biz.WebsiteRepo {
|
||||
return &websiteRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
cache: cache,
|
||||
database: database,
|
||||
databaseServer: databaseServer,
|
||||
@@ -232,7 +235,7 @@ func (r *websiteRepo) List(typ string, page, limit uint) ([]*biz.Website, int64,
|
||||
return websites, total, nil
|
||||
}
|
||||
|
||||
func (r *websiteRepo) Create(req *request.WebsiteCreate) (*biz.Website, error) {
|
||||
func (r *websiteRepo) Create(ctx context.Context, req *request.WebsiteCreate) (*biz.Website, error) {
|
||||
w := &biz.Website{
|
||||
Name: req.Name,
|
||||
Type: biz.WebsiteType(req.Type),
|
||||
@@ -425,6 +428,9 @@ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("website created", slog.String("type", biz.OperationTypeWebsite), slog.Uint64("operator_id", getOperatorID(ctx)), slog.String("name", req.Name), slog.String("website_type", req.Type), slog.String("path", req.Path))
|
||||
|
||||
// 重载 Web 服务器
|
||||
if err = r.reloadWebServer(); err != nil {
|
||||
return nil, err
|
||||
@@ -437,7 +443,7 @@ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
if err != nil {
|
||||
return nil, errors.New(r.t.Get("can't find %s database server, please add it first", name))
|
||||
}
|
||||
if err = r.database.Create(&request.DatabaseCreate{
|
||||
if err = r.database.Create(ctx, &request.DatabaseCreate{
|
||||
ServerID: server.ID,
|
||||
Name: req.DBName,
|
||||
CreateUser: true,
|
||||
@@ -453,7 +459,7 @@ location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.env) {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (r *websiteRepo) Update(req *request.WebsiteUpdate) error {
|
||||
func (r *websiteRepo) Update(ctx context.Context, req *request.WebsiteUpdate) error {
|
||||
website := new(biz.Website)
|
||||
if err := r.db.Where("id", req.ID).First(website).Error; err != nil {
|
||||
return err
|
||||
@@ -585,10 +591,13 @@ func (r *websiteRepo) Update(req *request.WebsiteUpdate) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("website updated", slog.String("type", biz.OperationTypeWebsite), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", website.Name))
|
||||
|
||||
return r.reloadWebServer()
|
||||
}
|
||||
|
||||
func (r *websiteRepo) Delete(req *request.WebsiteDelete) error {
|
||||
func (r *websiteRepo) Delete(ctx context.Context, req *request.WebsiteDelete) error {
|
||||
website := new(biz.Website)
|
||||
if err := r.db.Preload("Cert").Where("id", req.ID).First(website).Error; err != nil {
|
||||
return err
|
||||
@@ -605,11 +614,11 @@ func (r *websiteRepo) Delete(req *request.WebsiteDelete) error {
|
||||
if req.DB {
|
||||
if mysql, err := r.databaseServer.GetByName("local_mysql"); err == nil {
|
||||
_ = r.databaseUser.DeleteByNames(mysql.ID, []string{website.Name})
|
||||
_ = r.database.Delete(mysql.ID, website.Name)
|
||||
_ = r.database.Delete(ctx, mysql.ID, website.Name)
|
||||
}
|
||||
if postgres, err := r.databaseServer.GetByName("local_postgresql"); err == nil {
|
||||
_ = r.databaseUser.DeleteByNames(postgres.ID, []string{website.Name})
|
||||
_ = r.database.Delete(postgres.ID, website.Name)
|
||||
_ = r.database.Delete(ctx, postgres.ID, website.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,6 +626,9 @@ func (r *websiteRepo) Delete(req *request.WebsiteDelete) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
r.log.Info("website deleted", slog.String("type", biz.OperationTypeWebsite), slog.Uint64("operator_id", getOperatorID(ctx)), slog.Uint64("id", uint64(req.ID)), slog.String("name", website.Name))
|
||||
|
||||
return r.reloadWebServer()
|
||||
}
|
||||
|
||||
@@ -791,7 +803,7 @@ func (r *websiteRepo) ObtainCert(ctx context.Context, id uint) error {
|
||||
newCert, err := r.cert.GetByWebsite(website.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
newCert, err = r.cert.Create(&request.CertCreate{
|
||||
newCert, err = r.cert.Create(ctx, &request.CertCreate{
|
||||
Type: string(acme.KeyEC256),
|
||||
Domains: website.Domains,
|
||||
AutoRenewal: true,
|
||||
|
||||
7
internal/http/request/log.go
Normal file
7
internal/http/request/log.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package request
|
||||
|
||||
// LogList 日志列表请求
|
||||
type LogList struct {
|
||||
Type string `json:"type" form:"type" query:"type" validate:"required|in:app,db,http"`
|
||||
Limit int `json:"limit" form:"limit" query:"limit" validate:"min:1|max:1000"`
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (r *CertRenew) Run() {
|
||||
|
||||
var certs []biz.Cert
|
||||
if err := r.db.Preload("Website").Preload("Account").Preload("DNS").Find(&certs).Error; err != nil {
|
||||
r.log.Warn("[CertRenew] failed to get certs", slog.Any("err", err))
|
||||
r.log.Warn("failed to get certs", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (r *CertRenew) Run() {
|
||||
if cert.RenewalInfo.NeedsRefresh() {
|
||||
renewInfo, err := r.certRepo.RefreshRenewalInfo(cert.ID)
|
||||
if err != nil {
|
||||
r.log.Warn("[CertRenew] failed to refresh renewal info", slog.Any("err", err))
|
||||
r.log.Warn("failed to refresh renewal info", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
continue
|
||||
}
|
||||
cert.RenewalInfo = renewInfo
|
||||
@@ -67,7 +67,7 @@ func (r *CertRenew) Run() {
|
||||
// 到达建议时间,续签证书
|
||||
if time.Now().After(cert.RenewalInfo.SelectedTime) {
|
||||
if _, err := r.certRepo.Renew(cert.ID); err != nil {
|
||||
r.log.Warn("[CertRenew] failed to renew cert", slog.Any("err", err))
|
||||
r.log.Warn("failed to renew cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func (r *CertRenew) Run() {
|
||||
if r.conf.HTTP.ACME {
|
||||
decode, err := pkgcert.ParseCert(filepath.Join(app.Root, "panel/storage/cert.pem"))
|
||||
if err != nil {
|
||||
r.log.Warn("[CertRenew] failed to parse panel cert", slog.Any("err", err))
|
||||
r.log.Warn("failed to parse panel cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
// 结束时间大于 2 天不续签
|
||||
@@ -86,28 +86,28 @@ func (r *CertRenew) Run() {
|
||||
|
||||
ip, err := r.settingRepo.Get(biz.SettingKeyPublicIPs)
|
||||
if err != nil {
|
||||
r.log.Warn("[CertRenew] failed to get panel IP", slog.Any("err", err))
|
||||
r.log.Warn("failed to get panel IP", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
var ips []string
|
||||
if err = json.Unmarshal([]byte(ip), &ips); err != nil || len(ips) == 0 {
|
||||
r.log.Warn("[CertRenew] panel public IPs not set", slog.Any("err", err))
|
||||
r.log.Warn("panel public IPs not set", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
var user biz.User
|
||||
if err = r.db.First(&user).Error; err != nil {
|
||||
r.log.Warn("[CertRenew] failed to get a panel user", slog.Any("err", err))
|
||||
r.log.Warn("failed to get a panel user", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
account, err := r.certAccountRepo.GetDefault(user.ID)
|
||||
if err != nil {
|
||||
r.log.Warn("[CertRenew] failed to get panel ACME account", slog.Any("err", err))
|
||||
r.log.Warn("failed to get panel ACME account", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
crt, key, err := r.certRepo.ObtainPanel(account, ips)
|
||||
if err != nil {
|
||||
r.log.Warn("[CertRenew] failed to obtain ACME cert", slog.Any("err", err))
|
||||
r.log.Warn("failed to obtain ACME cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,11 +115,11 @@ func (r *CertRenew) Run() {
|
||||
Cert: string(crt),
|
||||
Key: string(key),
|
||||
}); err != nil {
|
||||
r.log.Warn("[CertRenew] failed to update panel cert", slog.Any("err", err))
|
||||
r.log.Warn("failed to update panel cert", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
r.log.Info("[CertRenew] panel cert renewed successfully")
|
||||
r.log.Info("panel cert renewed successfully", slog.String("type", biz.OperationTypeCert), slog.Uint64("operator_id", 0))
|
||||
tools.RestartPanel()
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func (r *Monitoring) Run() {
|
||||
}
|
||||
|
||||
if err = r.db.Create(&biz.Monitor{Info: info}).Error; err != nil {
|
||||
r.log.Warn("[Monitor] failed to create monitor record", slog.Any("err", err))
|
||||
r.log.Warn("failed to create monitor record", slog.String("type", biz.OperationTypeMonitor), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (r *Monitoring) Run() {
|
||||
return
|
||||
}
|
||||
if err = r.db.Where("created_at < ?", time.Now().AddDate(0, 0, -day).Format(time.DateTime)).Delete(&biz.Monitor{}).Error; err != nil {
|
||||
r.log.Warn("[Monitor] failed to delete monitor record", slog.Any("err", err))
|
||||
r.log.Warn("failed to delete monitor record", slog.String("type", biz.OperationTypeMonitor), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package job
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/rand/v2"
|
||||
@@ -49,29 +50,29 @@ func (r *PanelTask) Run() {
|
||||
// 优化数据库
|
||||
if err := r.db.Exec("VACUUM").Error; err != nil {
|
||||
app.Status = app.StatusFailed
|
||||
r.log.Warn("[PanelTask] failed to vacuum database", slog.Any("err", err))
|
||||
r.log.Warn("failed to vacuum database", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
if err := r.db.Exec("PRAGMA journal_mode=WAL;").Error; err != nil {
|
||||
app.Status = app.StatusFailed
|
||||
r.log.Warn("[PanelTask] failed to set database journal_mode to WAL", slog.Any("err", err))
|
||||
r.log.Warn("failed to set database journal_mode to WAL", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
if err := r.db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil {
|
||||
app.Status = app.StatusFailed
|
||||
r.log.Warn("[PanelTask] failed to wal checkpoint database", slog.Any("err", err))
|
||||
r.log.Warn("failed to wal checkpoint database", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 备份面板
|
||||
if err := r.backupRepo.Create(biz.BackupTypePanel, ""); err != nil {
|
||||
r.log.Warn("[PanelTask] failed to backup panel", slog.Any("err", err))
|
||||
if err := r.backupRepo.Create(context.Background(), biz.BackupTypePanel, ""); err != nil {
|
||||
r.log.Warn("failed to backup panel", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
|
||||
// 清理备份
|
||||
if path, err := r.backupRepo.GetPath("panel"); err == nil {
|
||||
if err = r.backupRepo.ClearExpired(path, "panel_", 10); err != nil {
|
||||
r.log.Warn("[PanelTask] failed to clear backup", slog.Any("err", err))
|
||||
r.log.Warn("failed to clear backup", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ func (r *PanelTask) Run() {
|
||||
func (r *PanelTask) updateCategories() {
|
||||
time.AfterFunc(time.Duration(rand.IntN(300))*time.Second, func() {
|
||||
if err := r.cacheRepo.UpdateCategories(); err != nil {
|
||||
r.log.Warn("[PanelTask] failed to update categories cache", slog.Any("err", err))
|
||||
r.log.Warn("failed to update categories cache", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -105,7 +106,7 @@ func (r *PanelTask) updateCategories() {
|
||||
func (r *PanelTask) updateApps() {
|
||||
time.AfterFunc(time.Duration(rand.IntN(300))*time.Second, func() {
|
||||
if err := r.cacheRepo.UpdateApps(); err != nil {
|
||||
r.log.Warn("[PanelTask] failed to update apps cache", slog.Any("err", err))
|
||||
r.log.Warn("failed to update apps cache", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -114,7 +115,7 @@ func (r *PanelTask) updateApps() {
|
||||
func (r *PanelTask) updateRewrites() {
|
||||
time.AfterFunc(time.Duration(rand.IntN(300))*time.Second, func() {
|
||||
if err := r.cacheRepo.UpdateRewrites(); err != nil {
|
||||
r.log.Warn("[PanelTask] failed to update rewrites cache", slog.Any("err", err))
|
||||
r.log.Warn("failed to update rewrites cache", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -148,7 +149,7 @@ func (r *PanelTask) updatePanel() {
|
||||
url := fmt.Sprintf("https://%s%s", r.conf.App.DownloadEndpoint, download.URL)
|
||||
checksum := fmt.Sprintf("https://%s%s", r.conf.App.DownloadEndpoint, download.Checksum)
|
||||
if err = r.backupRepo.UpdatePanel(panel.Version, url, checksum); err != nil {
|
||||
r.log.Warn("[PanelTask] failed to update panel", slog.Any("err", err))
|
||||
r.log.Warn("failed to update panel", slog.String("type", biz.OperationTypePanel), slog.Uint64("operator_id", 0), slog.Any("err", err))
|
||||
_ = r.backupRepo.FixPanel()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ type Http struct {
|
||||
containerImage *service.ContainerImageService
|
||||
containerVolume *service.ContainerVolumeService
|
||||
file *service.FileService
|
||||
log *service.LogService
|
||||
monitor *service.MonitorService
|
||||
setting *service.SettingService
|
||||
systemctl *service.SystemctlService
|
||||
@@ -85,6 +86,7 @@ func NewHttp(
|
||||
containerImage *service.ContainerImageService,
|
||||
containerVolume *service.ContainerVolumeService,
|
||||
file *service.FileService,
|
||||
log *service.LogService,
|
||||
monitor *service.MonitorService,
|
||||
setting *service.SettingService,
|
||||
systemctl *service.SystemctlService,
|
||||
@@ -125,6 +127,7 @@ func NewHttp(
|
||||
containerImage: containerImage,
|
||||
containerVolume: containerVolume,
|
||||
file: file,
|
||||
log: log,
|
||||
monitor: monitor,
|
||||
setting: setting,
|
||||
systemctl: systemctl,
|
||||
@@ -428,6 +431,10 @@ func (route *Http) Register(r *chi.Mux) {
|
||||
r.Get("/list", route.file.List)
|
||||
})
|
||||
|
||||
r.Route("/log", func(r chi.Router) {
|
||||
r.Get("/list", route.log.List)
|
||||
})
|
||||
|
||||
r.Route("/monitor", func(r chi.Router) {
|
||||
r.Get("/setting", route.monitor.GetSetting)
|
||||
r.Post("/setting", route.monitor.UpdateSetting)
|
||||
|
||||
@@ -50,7 +50,7 @@ func (s *BackupService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.backupRepo.Create(biz.BackupType(req.Type), req.Target, req.Path); err != nil {
|
||||
if err = s.backupRepo.Create(r.Context(), biz.BackupType(req.Type), req.Target, req.Path); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func (s *BackupService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.backupRepo.Delete(biz.BackupType(req.Type), req.File); err != nil {
|
||||
if err = s.backupRepo.Delete(r.Context(), biz.BackupType(req.Type), req.File); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func (s *BackupService) Restore(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.backupRepo.Restore(biz.BackupType(req.Type), req.File, req.Target); err != nil {
|
||||
if err = s.backupRepo.Restore(r.Context(), biz.BackupType(req.Type), req.File, req.Target); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func (s *CertService) Upload(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := s.certRepo.Upload(req)
|
||||
cert, err := s.certRepo.Upload(r.Context(), req)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -159,7 +159,7 @@ func (s *CertService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := s.certRepo.Create(req)
|
||||
cert, err := s.certRepo.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -175,7 +175,7 @@ func (s *CertService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.certRepo.Update(req); err != nil {
|
||||
if err = s.certRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func (s *CertService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.certRepo.Delete(req.ID)
|
||||
err = s.certRepo.Delete(r.Context(), req.ID)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *CertAccountService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
account, err := s.certAccountRepo.Create(req)
|
||||
account, err := s.certAccountRepo.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -61,7 +61,7 @@ func (s *CertAccountService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.certAccountRepo.Update(req); err != nil {
|
||||
if err = s.certAccountRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func (s *CertAccountService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.certAccountRepo.Delete(req.ID); err != nil {
|
||||
if err = s.certAccountRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *CertDNSService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
certDNS, err := s.certDNSRepo.Create(req)
|
||||
certDNS, err := s.certDNSRepo.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -61,7 +61,7 @@ func (s *CertDNSService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.certDNSRepo.Update(req); err != nil {
|
||||
if err = s.certDNSRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func (s *CertDNSService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.certDNSRepo.Delete(req.ID); err != nil {
|
||||
if err = s.certDNSRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -542,7 +542,7 @@ func (s *CliService) WebsiteCreate(ctx context.Context, cmd *cli.Command) error
|
||||
DB: false,
|
||||
}
|
||||
|
||||
website, err := s.websiteRepo.Create(req)
|
||||
website, err := s.websiteRepo.Create(ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -560,7 +560,7 @@ func (s *CliService) WebsiteRemove(ctx context.Context, cmd *cli.Command) error
|
||||
ID: website.ID,
|
||||
}
|
||||
|
||||
if err = s.websiteRepo.Delete(req); err != nil {
|
||||
if err = s.websiteRepo.Delete(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -579,7 +579,7 @@ func (s *CliService) WebsiteDelete(ctx context.Context, cmd *cli.Command) error
|
||||
DB: true,
|
||||
}
|
||||
|
||||
if err = s.websiteRepo.Delete(req); err != nil {
|
||||
if err = s.websiteRepo.Delete(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -631,7 +631,7 @@ func (s *CliService) BackupWebsite(ctx context.Context, cmd *cli.Command) error
|
||||
fmt.Println(s.hr)
|
||||
fmt.Println(s.t.Get("|-Backup type: website"))
|
||||
fmt.Println(s.t.Get("|-Backup target: %s", cmd.String("name")))
|
||||
if err := s.backupRepo.Create(biz.BackupTypeWebsite, cmd.String("name"), cmd.String("path")); err != nil {
|
||||
if err := s.backupRepo.Create(ctx, biz.BackupTypeWebsite, cmd.String("name"), cmd.String("path")); err != nil {
|
||||
return errors.New(s.t.Get("Backup failed: %v", err))
|
||||
}
|
||||
fmt.Println(s.hr)
|
||||
@@ -647,7 +647,7 @@ func (s *CliService) BackupDatabase(ctx context.Context, cmd *cli.Command) error
|
||||
fmt.Println(s.t.Get("|-Backup type: database"))
|
||||
fmt.Println(s.t.Get("|-Database: %s", cmd.String("type")))
|
||||
fmt.Println(s.t.Get("|-Backup target: %s", cmd.String("name")))
|
||||
if err := s.backupRepo.Create(biz.BackupType(cmd.String("type")), cmd.String("name"), cmd.String("path")); err != nil {
|
||||
if err := s.backupRepo.Create(ctx, biz.BackupType(cmd.String("type")), cmd.String("name"), cmd.String("path")); err != nil {
|
||||
return errors.New(s.t.Get("Backup failed: %v", err))
|
||||
}
|
||||
fmt.Println(s.hr)
|
||||
@@ -661,7 +661,7 @@ func (s *CliService) BackupPanel(ctx context.Context, cmd *cli.Command) error {
|
||||
fmt.Println(s.t.Get("★ Start backup [%s]", time.Now().Format(time.DateTime)))
|
||||
fmt.Println(s.hr)
|
||||
fmt.Println(s.t.Get("|-Backup type: panel"))
|
||||
if err := s.backupRepo.Create(biz.BackupTypePanel, "", cmd.String("path")); err != nil {
|
||||
if err := s.backupRepo.Create(ctx, biz.BackupTypePanel, "", cmd.String("path")); err != nil {
|
||||
return errors.New(s.t.Get("Backup failed: %v", err))
|
||||
}
|
||||
fmt.Println(s.hr)
|
||||
@@ -948,7 +948,7 @@ func (s *CliService) Init(ctx context.Context, cmd *cli.Command) error {
|
||||
return errors.New(s.t.Get("Initialization failed: %v", err))
|
||||
}
|
||||
|
||||
_, err = s.userRepo.Create("admin", value, str.Random(8)+"@yourdomain.com")
|
||||
_, err = s.userRepo.Create(ctx, "admin", value, str.Random(8)+"@yourdomain.com")
|
||||
if err != nil {
|
||||
return errors.New(s.t.Get("Initialization failed: %v", err))
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *CronService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.cronRepo.Create(req); err != nil {
|
||||
if err = s.cronRepo.Create(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (s *CronService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.cronRepo.Update(req); err != nil {
|
||||
if err = s.cronRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func (s *CronService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.cronRepo.Delete(req.ID); err != nil {
|
||||
if err = s.cronRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *DatabaseService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseRepo.Create(req); err != nil {
|
||||
if err = s.databaseRepo.Create(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (s *DatabaseService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseRepo.Delete(req.ServerID, req.Name); err != nil {
|
||||
if err = s.databaseRepo.Delete(r.Context(), req.ServerID, req.Name); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *DatabaseUserService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseUserRepo.Create(req); err != nil {
|
||||
if err = s.databaseUserRepo.Create(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func (s *DatabaseUserService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.databaseUserRepo.Delete(req.ID); err != nil {
|
||||
if err = s.databaseUserRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
40
internal/service/log.go
Normal file
40
internal/service/log.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/acepanel/panel/internal/biz"
|
||||
"github.com/acepanel/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type LogService struct {
|
||||
logRepo biz.LogRepo
|
||||
}
|
||||
|
||||
func NewLogService(logRepo biz.LogRepo) *LogService {
|
||||
return &LogService{
|
||||
logRepo: logRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// List 获取日志列表
|
||||
func (s *LogService) List(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := Bind[request.LogList](r)
|
||||
if err != nil {
|
||||
Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 默认限制
|
||||
if req.Limit == 0 {
|
||||
req.Limit = 100
|
||||
}
|
||||
|
||||
entries, err := s.logRepo.List(req.Type, req.Limit)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
Success(w, entries)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func (s *ProjectService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
project, err := s.projectRepo.Create(req)
|
||||
project, err := s.projectRepo.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -79,7 +79,7 @@ func (s *ProjectService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.projectRepo.Update(req); err != nil {
|
||||
if err = s.projectRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func (s *ProjectService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.projectRepo.Delete(req.ID); err != nil {
|
||||
if err = s.projectRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func (s *SafeService) UpdateSSH(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.safeRepo.UpdateSSH(req.Port, req.Status); err != nil {
|
||||
if err = s.safeRepo.UpdateSSH(r.Context(), req.Port, req.Status); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func (s *SafeService) UpdatePingStatus(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.safeRepo.UpdatePingStatus(req.Status); err != nil {
|
||||
if err = s.safeRepo.UpdatePingStatus(r.Context(), req.Status); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ var ProviderSet = wire.NewSet(
|
||||
NewFileService,
|
||||
NewFirewallService,
|
||||
NewHomeService,
|
||||
NewLogService,
|
||||
NewMonitorService,
|
||||
NewProcessService,
|
||||
NewProjectService,
|
||||
|
||||
@@ -49,7 +49,7 @@ func (s *SettingService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
restart := false
|
||||
if restart, err = s.settingRepo.UpdatePanel(req); err != nil {
|
||||
if restart, err = s.settingRepo.UpdatePanel(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *SSHService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.sshRepo.Create(req); err != nil {
|
||||
if err = s.sshRepo.Create(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (s *SSHService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.sshRepo.Update(req); err != nil {
|
||||
if err = s.sshRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func (s *SSHService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.sshRepo.Delete(req.ID); err != nil {
|
||||
if err = s.sshRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ func (s *UserService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := s.userRepo.Create(req.Username, req.Password, req.Email)
|
||||
user, err := s.userRepo.Create(r.Context(), req.Username, req.Password, req.Email)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -281,7 +281,7 @@ func (s *UserService) UpdateUsername(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.userRepo.UpdateUsername(req.ID, req.Username); err != nil {
|
||||
if err = s.userRepo.UpdateUsername(r.Context(), req.ID, req.Username); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func (s *UserService) UpdatePassword(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.userRepo.UpdatePassword(req.ID, req.Password); err != nil {
|
||||
if err = s.userRepo.UpdatePassword(r.Context(), req.ID, req.Password); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -311,7 +311,7 @@ func (s *UserService) UpdateEmail(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.userRepo.UpdateEmail(req.ID, req.Email); err != nil {
|
||||
if err = s.userRepo.UpdateEmail(r.Context(), req.ID, req.Email); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -367,7 +367,7 @@ func (s *UserService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.userRepo.Delete(req.ID); err != nil {
|
||||
if err = s.userRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (s *WebHookService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
webhook, err := s.webhookRepo.Create(req)
|
||||
webhook, err := s.webhookRepo.Create(r.Context(), req)
|
||||
if err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -77,7 +77,7 @@ func (s *WebHookService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.webhookRepo.Update(req); err != nil {
|
||||
if err = s.webhookRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -92,7 +92,7 @@ func (s *WebHookService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.webhookRepo.Delete(req.ID); err != nil {
|
||||
if err = s.webhookRepo.Delete(r.Context(), req.ID); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (s *WebsiteService) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req.Path = filepath.Join(req.Path, req.Name, "public")
|
||||
}
|
||||
|
||||
if _, err = s.websiteRepo.Create(req); err != nil {
|
||||
if _, err = s.websiteRepo.Create(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func (s *WebsiteService) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.websiteRepo.Update(req); err != nil {
|
||||
if err = s.websiteRepo.Update(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func (s *WebsiteService) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.websiteRepo.Delete(req); err != nil {
|
||||
if err = s.websiteRepo.Delete(r.Context(), req); err != nil {
|
||||
Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
7
web/src/api/panel/log/index.ts
Normal file
7
web/src/api/panel/log/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { http } from '@/utils'
|
||||
|
||||
export default {
|
||||
// 获取日志列表
|
||||
list: (type: 'app' | 'db' | 'http', limit: number = 100): any =>
|
||||
http.Get('/log/list', { params: { type, limit } })
|
||||
}
|
||||
121
web/src/views/log/DatabaseLog.vue
Normal file
121
web/src/views/log/DatabaseLog.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'database-log'
|
||||
})
|
||||
|
||||
import { NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import log from '@/api/panel/log'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
// 日志条目类型定义
|
||||
interface LogEntry {
|
||||
time: string
|
||||
level: string
|
||||
msg: string
|
||||
extra?: Record<string, any>
|
||||
}
|
||||
|
||||
// 数据加载
|
||||
const limit = ref(200)
|
||||
const { loading, data, send: refresh } = useRequest(
|
||||
() => log.list('db', limit.value),
|
||||
{ initialData: [] }
|
||||
)
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: $gettext('Time'),
|
||||
key: 'time',
|
||||
width: 180,
|
||||
render: (row: LogEntry) => {
|
||||
const date = new Date(row.time)
|
||||
return date.toLocaleString()
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Level'),
|
||||
key: 'level',
|
||||
width: 80,
|
||||
render: (row: LogEntry) => {
|
||||
const typeMap: Record<string, 'success' | 'warning' | 'error' | 'info'> = {
|
||||
INFO: 'success',
|
||||
WARN: 'warning',
|
||||
ERROR: 'error',
|
||||
DEBUG: 'info'
|
||||
}
|
||||
return h(NTag, { type: typeMap[row.level] || 'default', size: 'small' }, () => row.level)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Query'),
|
||||
key: 'query',
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
},
|
||||
render: (row: LogEntry) => {
|
||||
return row.extra?.query || row.msg || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Duration'),
|
||||
key: 'duration',
|
||||
width: 120,
|
||||
render: (row: LogEntry) => {
|
||||
if (row.extra?.duration) {
|
||||
// 纳秒转毫秒
|
||||
const ms = Number(row.extra.duration) / 1000000
|
||||
return `${ms.toFixed(2)} ms`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Rows'),
|
||||
key: 'rows',
|
||||
width: 80,
|
||||
render: (row: LogEntry) => {
|
||||
return row.extra?.rows !== undefined ? row.extra.rows : '-'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
refresh()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="mb-4 flex gap-4 items-center">
|
||||
<span>{{ $gettext('Show entries') }}:</span>
|
||||
<n-select
|
||||
v-model:value="limit"
|
||||
:options="[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: '200', value: 200 },
|
||||
{ label: '500', value: 500 },
|
||||
{ label: '1000', value: 1000 }
|
||||
]"
|
||||
class="w-100px"
|
||||
@update:value="handleRefresh"
|
||||
/>
|
||||
<n-button type="primary" @click="handleRefresh">
|
||||
{{ $gettext('Refresh') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:loading="loading"
|
||||
:bordered="false"
|
||||
:max-height="600"
|
||||
:scroll-x="800"
|
||||
virtual-scroll
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
146
web/src/views/log/HttpLog.vue
Normal file
146
web/src/views/log/HttpLog.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'http-log'
|
||||
})
|
||||
|
||||
import { NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import log from '@/api/panel/log'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
// 日志条目类型定义
|
||||
interface LogEntry {
|
||||
time: string
|
||||
level: string
|
||||
msg: string
|
||||
extra?: Record<string, any>
|
||||
}
|
||||
|
||||
// 数据加载
|
||||
const limit = ref(200)
|
||||
const { loading, data, send: refresh } = useRequest(
|
||||
() => log.list('http', limit.value),
|
||||
{ initialData: [] }
|
||||
)
|
||||
|
||||
// 获取状态码颜色
|
||||
const getStatusType = (code: number): 'success' | 'warning' | 'error' | 'info' => {
|
||||
if (code >= 200 && code < 300) return 'success'
|
||||
if (code >= 300 && code < 400) return 'info'
|
||||
if (code >= 400 && code < 500) return 'warning'
|
||||
return 'error'
|
||||
}
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: $gettext('Time'),
|
||||
key: 'time',
|
||||
width: 180,
|
||||
render: (row: LogEntry) => {
|
||||
const date = new Date(row.time)
|
||||
return date.toLocaleString()
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Method'),
|
||||
key: 'method',
|
||||
width: 80,
|
||||
render: (row: LogEntry) => {
|
||||
const method = row.extra?.['http.request.method'] || '-'
|
||||
const colorMap: Record<string, string> = {
|
||||
GET: '#52c41a',
|
||||
POST: '#1890ff',
|
||||
PUT: '#faad14',
|
||||
DELETE: '#ff4d4f',
|
||||
PATCH: '#722ed1'
|
||||
}
|
||||
return h('span', { style: { color: colorMap[method] || 'inherit', fontWeight: 'bold' } }, method)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Path'),
|
||||
key: 'path',
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
},
|
||||
render: (row: LogEntry) => {
|
||||
return row.extra?.['url.path'] || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Status'),
|
||||
key: 'status',
|
||||
width: 80,
|
||||
render: (row: LogEntry) => {
|
||||
const status = row.extra?.['http.response.status_code']
|
||||
if (status) {
|
||||
return h(NTag, { type: getStatusType(status), size: 'small' }, () => status)
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Duration'),
|
||||
key: 'duration',
|
||||
width: 120,
|
||||
render: (row: LogEntry) => {
|
||||
const duration = row.extra?.['event.duration']
|
||||
if (duration) {
|
||||
// 纳秒转毫秒
|
||||
const ms = Number(duration) / 1000000
|
||||
return `${ms.toFixed(2)} ms`
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Client IP'),
|
||||
key: 'client_ip',
|
||||
width: 150,
|
||||
render: (row: LogEntry) => {
|
||||
const ip = row.extra?.['client.ip'] || '-'
|
||||
// 移除端口号
|
||||
return typeof ip === 'string' && ip.includes(':') ? ip.split(':')[0] : ip
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
refresh()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="mb-4 flex gap-4 items-center">
|
||||
<span>{{ $gettext('Show entries') }}:</span>
|
||||
<n-select
|
||||
v-model:value="limit"
|
||||
:options="[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: '200', value: 200 },
|
||||
{ label: '500', value: 500 },
|
||||
{ label: '1000', value: 1000 }
|
||||
]"
|
||||
class="w-100px"
|
||||
@update:value="handleRefresh"
|
||||
/>
|
||||
<n-button type="primary" @click="handleRefresh">
|
||||
{{ $gettext('Refresh') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:loading="loading"
|
||||
:bordered="false"
|
||||
:max-height="600"
|
||||
:scroll-x="800"
|
||||
virtual-scroll
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
34
web/src/views/log/IndexView.vue
Normal file
34
web/src/views/log/IndexView.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'log-index'
|
||||
})
|
||||
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import DatabaseLog from './DatabaseLog.vue'
|
||||
import HttpLog from './HttpLog.vue'
|
||||
import OperationLog from './OperationLog.vue'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
// 当前激活的标签
|
||||
const activeTab = ref('operation')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<common-page show-header show-footer>
|
||||
<n-tabs v-model:value="activeTab" type="line" animated>
|
||||
<n-tab-pane name="operation" :tab="$gettext('Operation Log')">
|
||||
<OperationLog />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="database" :tab="$gettext('Database Log')">
|
||||
<DatabaseLog />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="http" :tab="$gettext('HTTP Log')">
|
||||
<HttpLog />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</common-page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
119
web/src/views/log/OperationLog.vue
Normal file
119
web/src/views/log/OperationLog.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'operation-log'
|
||||
})
|
||||
|
||||
import { NTag } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import log from '@/api/panel/log'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
|
||||
// 日志条目类型定义
|
||||
interface LogEntry {
|
||||
time: string
|
||||
level: string
|
||||
msg: string
|
||||
type?: string
|
||||
operator_id?: number
|
||||
operator_name?: string
|
||||
extra?: Record<string, any>
|
||||
}
|
||||
|
||||
// 数据加载
|
||||
const limit = ref(200)
|
||||
const { loading, data, send: refresh } = useRequest(
|
||||
() => log.list('app', limit.value),
|
||||
{ initialData: [] }
|
||||
)
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: $gettext('Time'),
|
||||
key: 'time',
|
||||
width: 180,
|
||||
render: (row: LogEntry) => {
|
||||
const date = new Date(row.time)
|
||||
return date.toLocaleString()
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Level'),
|
||||
key: 'level',
|
||||
width: 80,
|
||||
render: (row: LogEntry) => {
|
||||
const typeMap: Record<string, 'success' | 'warning' | 'error' | 'info'> = {
|
||||
INFO: 'success',
|
||||
WARN: 'warning',
|
||||
ERROR: 'error',
|
||||
DEBUG: 'info'
|
||||
}
|
||||
return h(NTag, { type: typeMap[row.level] || 'default', size: 'small' }, () => row.level)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Type'),
|
||||
key: 'type',
|
||||
width: 120,
|
||||
render: (row: LogEntry) => {
|
||||
return row.type || '-'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Operator'),
|
||||
key: 'operator_name',
|
||||
width: 120,
|
||||
render: (row: LogEntry) => {
|
||||
if (row.operator_id === 0 || row.operator_id === undefined) {
|
||||
return $gettext('System')
|
||||
}
|
||||
return row.operator_name || `#${row.operator_id}`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: $gettext('Message'),
|
||||
key: 'msg',
|
||||
ellipsis: {
|
||||
tooltip: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
refresh()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="mb-4 flex gap-4 items-center">
|
||||
<span>{{ $gettext('Show entries') }}:</span>
|
||||
<n-select
|
||||
v-model:value="limit"
|
||||
:options="[
|
||||
{ label: '100', value: 100 },
|
||||
{ label: '200', value: 200 },
|
||||
{ label: '500', value: 500 },
|
||||
{ label: '1000', value: 1000 }
|
||||
]"
|
||||
class="w-100px"
|
||||
@update:value="handleRefresh"
|
||||
/>
|
||||
<n-button type="primary" @click="handleRefresh">
|
||||
{{ $gettext('Refresh') }}
|
||||
</n-button>
|
||||
</div>
|
||||
<n-data-table
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:loading="loading"
|
||||
:bordered="false"
|
||||
:max-height="600"
|
||||
:scroll-x="800"
|
||||
virtual-scroll
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
25
web/src/views/log/route.ts
Normal file
25
web/src/views/log/route.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { RouteType } from '~/types/router'
|
||||
|
||||
const Layout = () => import('@/layout/IndexView.vue')
|
||||
|
||||
export default {
|
||||
name: 'log',
|
||||
path: '/log',
|
||||
component: Layout,
|
||||
meta: {
|
||||
order: 35
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'log-index',
|
||||
path: '',
|
||||
component: () => import('./IndexView.vue'),
|
||||
meta: {
|
||||
title: 'Logs',
|
||||
icon: 'mdi:file-document-outline',
|
||||
role: ['admin'],
|
||||
requireAuth: true
|
||||
}
|
||||
}
|
||||
]
|
||||
} as RouteType
|
||||
Reference in New Issue
Block a user