diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 05b5556f..ee02fc9c 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -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) diff --git a/cmd/cli/wire_gen.go b/cmd/cli/wire_gen.go index 00b6d76b..536c465a 100644 --- a/cmd/cli/wire_gen.go +++ b/cmd/cli/wire_gen.go @@ -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) diff --git a/internal/biz/backup.go b/internal/biz/backup.go index 1e9e0056..77b85057 100644 --- a/internal/biz/backup.go +++ b/internal/biz/backup.go @@ -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) diff --git a/internal/biz/cert.go b/internal/biz/cert.go index 707ee4f5..2d05cb51 100644 --- a/internal/biz/cert.go +++ b/internal/biz/cert.go @@ -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) diff --git a/internal/biz/cert_account.go b/internal/biz/cert_account.go index d889979d..428454ba 100644 --- a/internal/biz/cert_account.go +++ b/internal/biz/cert_account.go @@ -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 } diff --git a/internal/biz/cert_dns.go b/internal/biz/cert_dns.go index 29e4b125..781cda7f 100644 --- a/internal/biz/cert_dns.go +++ b/internal/biz/cert_dns.go @@ -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 } diff --git a/internal/biz/cron.go b/internal/biz/cron.go index ea0ac25b..ff627767 100644 --- a/internal/biz/cron.go +++ b/internal/biz/cron.go @@ -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 } diff --git a/internal/biz/database.go b/internal/biz/database.go index 5acbf099..39158308 100644 --- a/internal/biz/database.go +++ b/internal/biz/database.go @@ -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 } diff --git a/internal/biz/database_user.go b/internal/biz/database_user.go index 855c80ee..973fa24d 100644 --- a/internal/biz/database_user.go +++ b/internal/biz/database_user.go @@ -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 } diff --git a/internal/biz/log.go b/internal/biz/log.go new file mode 100644 index 00000000..1a460eb6 --- /dev/null +++ b/internal/biz/log.go @@ -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) +} diff --git a/internal/biz/project.go b/internal/biz/project.go index dce55e7a..458750c1 100644 --- a/internal/biz/project.go +++ b/internal/biz/project.go @@ -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 } diff --git a/internal/biz/safe.go b/internal/biz/safe.go index f9ba6fe5..4d55c3c3 100644 --- a/internal/biz/safe.go +++ b/internal/biz/safe.go @@ -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 } diff --git a/internal/biz/setting.go b/internal/biz/setting.go index 7dd8d0c5..755a0c0a 100644 --- a/internal/biz/setting.go +++ b/internal/biz/setting.go @@ -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 } diff --git a/internal/biz/ssh.go b/internal/biz/ssh.go index 19a5a662..11b94537 100644 --- a/internal/biz/ssh.go +++ b/internal/biz/ssh.go @@ -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 } diff --git a/internal/biz/user.go b/internal/biz/user.go index 3432ac24..a30fcc08 100644 --- a/internal/biz/user.go +++ b/internal/biz/user.go @@ -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) diff --git a/internal/biz/webhook.go b/internal/biz/webhook.go index fca5b016..e43bb708 100644 --- a/internal/biz/webhook.go +++ b/internal/biz/webhook.go @@ -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) } diff --git a/internal/biz/website.go b/internal/biz/website.go index 6cba72ee..2f69366a 100644 --- a/internal/biz/website.go +++ b/internal/biz/website.go @@ -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 diff --git a/internal/data/app.go b/internal/data/app.go index ae8969b8..4a1b153d 100644 --- a/internal/data/app.go +++ b/internal/data/app.go @@ -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 { diff --git a/internal/data/backup.go b/internal/data/backup.go index 1e3f6b2a..e4d23d22 100644 --- a/internal/data/backup.go +++ b/internal/data/backup.go @@ -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 { diff --git a/internal/data/cert.go b/internal/data/cert.go index 0ce34a3c..c9f04303 100644 --- a/internal/data/cert.go +++ b/internal/data/cert.go @@ -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) { diff --git a/internal/data/cert_account.go b/internal/data/cert_account.go index f272361a..1793df94 100644 --- a/internal/data/cert_account.go +++ b/internal/data/cert_account.go @@ -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 diff --git a/internal/data/cert_dns.go b/internal/data/cert_dns.go index 57ea2d2c..3a437109 100644 --- a/internal/data/cert_dns.go +++ b/internal/data/cert_dns.go @@ -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 } diff --git a/internal/data/cron.go b/internal/data/cron.go index 4a27fb19..405663eb 100644 --- a/internal/data/cron.go +++ b/internal/data/cron.go @@ -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 { diff --git a/internal/data/data.go b/internal/data/data.go index b848a8b9..bc57b283 100644 --- a/internal/data/data.go +++ b/internal/data/data.go @@ -20,6 +20,7 @@ var ProviderSet = wire.NewSet( NewDatabaseServerRepo, NewDatabaseUserRepo, NewEnvironmentRepo, + NewLogRepo, NewMonitorRepo, NewProjectRepo, NewSafeRepo, diff --git a/internal/data/database.go b/internal/data/database.go index eb1ffaf0..51351e0e 100644 --- a/internal/data/database.go +++ b/internal/data/database.go @@ -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 { diff --git a/internal/data/database_server.go b/internal/data/database_server.go index 5ecf7f52..45648077 100644 --- a/internal/data/database_server.go +++ b/internal/data/database_server.go @@ -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)) } } } diff --git a/internal/data/database_user.go b/internal/data/database_user.go index c209fcc4..eff969eb 100644 --- a/internal/data/database_user.go +++ b/internal/data/database_user.go @@ -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 { diff --git a/internal/data/helper.go b/internal/data/helper.go index 3e9c47ff..97784c2d 100644 --- a/internal/data/helper.go +++ b/internal/data/helper.go @@ -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) +} diff --git a/internal/data/log.go b/internal/data/log.go new file mode 100644 index 00000000..21e4dfeb --- /dev/null +++ b/internal/data/log.go @@ -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 +} diff --git a/internal/data/project.go b/internal/data/project.go index ac93129b..6664071d 100644 --- a/internal/data/project.go +++ b/internal/data/project.go @@ -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 文件路径 diff --git a/internal/data/safe.go b/internal/data/safe.go index 3e1634b5..6997c517 100644 --- a/internal/data/safe.go +++ b/internal/data/safe.go @@ -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 } diff --git a/internal/data/setting.go b/internal/data/setting.go index d23df387..be4e71ef 100644 --- a/internal/data/setting.go +++ b/internal/data/setting.go @@ -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 } diff --git a/internal/data/ssh.go b/internal/data/ssh.go index ae130664..1b65aacb 100644 --- a/internal/data/ssh.go +++ b/internal/data/ssh.go @@ -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 } diff --git a/internal/data/user.go b/internal/data/user.go index ed683e0f..4ad42866 100644 --- a/internal/data/user.go +++ b/internal/data/user.go @@ -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) { diff --git a/internal/data/webhook.go b/internal/data/webhook.go index c8638951..b9aea287 100644 --- a/internal/data/webhook.go +++ b/internal/data/webhook.go @@ -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) { diff --git a/internal/data/website.go b/internal/data/website.go index a06e3880..4edf0db4 100644 --- a/internal/data/website.go +++ b/internal/data/website.go @@ -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, diff --git a/internal/http/request/log.go b/internal/http/request/log.go new file mode 100644 index 00000000..bf95a561 --- /dev/null +++ b/internal/http/request/log.go @@ -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"` +} diff --git a/internal/job/cert_renew.go b/internal/job/cert_renew.go index 05e5baf8..4dafcdfe 100644 --- a/internal/job/cert_renew.go +++ b/internal/job/cert_renew.go @@ -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() } diff --git a/internal/job/monitoring.go b/internal/job/monitoring.go index c749d6e6..d5f21d7b 100644 --- a/internal/job/monitoring.go +++ b/internal/job/monitoring.go @@ -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 } } diff --git a/internal/job/panel_task.go b/internal/job/panel_task.go index bfae9ade..b260d22a 100644 --- a/internal/job/panel_task.go +++ b/internal/job/panel_task.go @@ -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() } } diff --git a/internal/route/http.go b/internal/route/http.go index b5d7544e..d55a4b27 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -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) diff --git a/internal/service/backup.go b/internal/service/backup.go index fad26c10..3cc2194a 100644 --- a/internal/service/backup.go +++ b/internal/service/backup.go @@ -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 } diff --git a/internal/service/cert.go b/internal/service/cert.go index 6787e928..bda7671f 100644 --- a/internal/service/cert.go +++ b/internal/service/cert.go @@ -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 diff --git a/internal/service/cert_account.go b/internal/service/cert_account.go index 3e6f96f4..4e804936 100644 --- a/internal/service/cert_account.go +++ b/internal/service/cert_account.go @@ -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 } diff --git a/internal/service/cert_dns.go b/internal/service/cert_dns.go index 058ad718..a752f029 100644 --- a/internal/service/cert_dns.go +++ b/internal/service/cert_dns.go @@ -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 } diff --git a/internal/service/cli.go b/internal/service/cli.go index abaa879c..1393e1ab 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -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)) } diff --git a/internal/service/cron.go b/internal/service/cron.go index 4b36425f..75536743 100644 --- a/internal/service/cron.go +++ b/internal/service/cron.go @@ -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 } diff --git a/internal/service/database.go b/internal/service/database.go index fe47eafe..8aa5177e 100644 --- a/internal/service/database.go +++ b/internal/service/database.go @@ -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 } diff --git a/internal/service/database_user.go b/internal/service/database_user.go index 003d8fa6..28ebd030 100644 --- a/internal/service/database_user.go +++ b/internal/service/database_user.go @@ -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 } diff --git a/internal/service/log.go b/internal/service/log.go new file mode 100644 index 00000000..89dd74de --- /dev/null +++ b/internal/service/log.go @@ -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) +} diff --git a/internal/service/project.go b/internal/service/project.go index fa9af6ef..0b736531 100644 --- a/internal/service/project.go +++ b/internal/service/project.go @@ -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 } diff --git a/internal/service/safe.go b/internal/service/safe.go index 68a38f56..0f6bcdd8 100644 --- a/internal/service/safe.go +++ b/internal/service/safe.go @@ -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 } diff --git a/internal/service/service.go b/internal/service/service.go index d3253c76..d4c05719 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -24,6 +24,7 @@ var ProviderSet = wire.NewSet( NewFileService, NewFirewallService, NewHomeService, + NewLogService, NewMonitorService, NewProcessService, NewProjectService, diff --git a/internal/service/setting.go b/internal/service/setting.go index 8c03f73b..f8293eeb 100644 --- a/internal/service/setting.go +++ b/internal/service/setting.go @@ -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 } diff --git a/internal/service/ssh.go b/internal/service/ssh.go index 07399816..73bdc7df 100644 --- a/internal/service/ssh.go +++ b/internal/service/ssh.go @@ -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 } diff --git a/internal/service/user.go b/internal/service/user.go index 3ec6caf4..c9a45aaa 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -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 } diff --git a/internal/service/webhook.go b/internal/service/webhook.go index 8b8aee0c..e7cbbd49 100644 --- a/internal/service/webhook.go +++ b/internal/service/webhook.go @@ -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 } diff --git a/internal/service/website.go b/internal/service/website.go index 8c9cc4bc..dc9e877f 100644 --- a/internal/service/website.go +++ b/internal/service/website.go @@ -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 } diff --git a/web/src/api/panel/log/index.ts b/web/src/api/panel/log/index.ts new file mode 100644 index 00000000..9803f14a --- /dev/null +++ b/web/src/api/panel/log/index.ts @@ -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 } }) +} diff --git a/web/src/views/log/DatabaseLog.vue b/web/src/views/log/DatabaseLog.vue new file mode 100644 index 00000000..8fa24da2 --- /dev/null +++ b/web/src/views/log/DatabaseLog.vue @@ -0,0 +1,121 @@ + + + + + + {{ $gettext('Show entries') }}: + + + {{ $gettext('Refresh') }} + + + + + diff --git a/web/src/views/log/HttpLog.vue b/web/src/views/log/HttpLog.vue new file mode 100644 index 00000000..04fb2e62 --- /dev/null +++ b/web/src/views/log/HttpLog.vue @@ -0,0 +1,146 @@ + + + + + + {{ $gettext('Show entries') }}: + + + {{ $gettext('Refresh') }} + + + + + diff --git a/web/src/views/log/IndexView.vue b/web/src/views/log/IndexView.vue new file mode 100644 index 00000000..41c828ca --- /dev/null +++ b/web/src/views/log/IndexView.vue @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/src/views/log/OperationLog.vue b/web/src/views/log/OperationLog.vue new file mode 100644 index 00000000..dbcb7215 --- /dev/null +++ b/web/src/views/log/OperationLog.vue @@ -0,0 +1,119 @@ + + + + + + {{ $gettext('Show entries') }}: + + + {{ $gettext('Refresh') }} + + + + + diff --git a/web/src/views/log/route.ts b/web/src/views/log/route.ts new file mode 100644 index 00000000..f451e8c0 --- /dev/null +++ b/web/src/views/log/route.ts @@ -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