diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go
index a9281ed95..d19446ce9 100644
--- a/backend/app/service/cronjob_helper.go
+++ b/backend/app/service/cronjob_helper.go
@@ -3,9 +3,11 @@ package service
import (
"context"
"fmt"
+ "github.com/1Panel-dev/1Panel/backend/utils/files"
"os"
"path"
"strings"
+ "sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
@@ -54,6 +56,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
}
message = []byte(stdout)
u.HandleRmExpired("LOCAL", "", cronjob, nil)
+ case "cutWebsiteLog":
+ record.File, err = u.handleCutWebsiteLog(cronjob, record.StartTime)
+ if err != nil {
+ global.LOG.Errorf("cut website log file failed, err: %v", err)
+ }
}
if err != nil {
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
@@ -157,7 +164,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
if len(exclude) == 0 {
continue
}
- excludeRules += (" --exclude " + exclude)
+ excludeRules += " --exclude " + exclude
}
path := ""
if strings.Contains(sourceDir, "/") {
@@ -262,6 +269,72 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInf
return paths, nil
}
+func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time) (string, error) {
+ var (
+ websites []string
+ err error
+ filePaths []string
+ )
+ if cronjob.Website == "all" {
+ websites, _ = NewIWebsiteService().GetWebsiteOptions()
+ if len(websites) == 0 {
+ return "", nil
+ }
+ } else {
+ websites = append(websites, cronjob.Website)
+ }
+
+ nginx, err := getAppInstallByKey(constant.AppOpenresty)
+ if err != nil {
+ return "", nil
+ }
+ baseDir := path.Join(nginx.GetPath(), "www", "sites")
+ fileOp := files.NewFileOp()
+ var wg sync.WaitGroup
+ wg.Add(len(websites))
+ for _, websiteName := range websites {
+ name := websiteName
+ go func() {
+ website, _ := websiteRepo.GetFirst(websiteRepo.WithDomain(name))
+ if website.ID == 0 {
+ wg.Done()
+ return
+ }
+ websiteLogDir := path.Join(baseDir, website.PrimaryDomain, "log")
+ srcAccessLogPath := path.Join(websiteLogDir, "access.log")
+ srcErrorLogPath := path.Join(websiteLogDir, "error.log")
+ dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.PrimaryDomain)
+ if !fileOp.Stat(dstLogDir) {
+ _ = os.MkdirAll(dstLogDir, 0755)
+ }
+
+ dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
+ filePaths = append(filePaths, path.Join(dstLogDir, dstName))
+ if err = fileOp.Compress([]string{srcAccessLogPath, srcErrorLogPath}, dstLogDir, dstName, files.Gz); err != nil {
+ global.LOG.Errorf("There was an error in compressing the website[%s] access.log", website.PrimaryDomain)
+ } else {
+ _ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), 0755)
+ _ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
+ }
+ global.LOG.Infof("The website[%s] log file was successfully rotated in the directory [%s]", website.PrimaryDomain, dstLogDir)
+ var record model.BackupRecord
+ record.Type = "cutWebsiteLog"
+ record.Name = cronjob.Website
+ record.Source = "LOCAL"
+ record.BackupType = "LOCAL"
+ record.FileDir = dstLogDir
+ record.FileName = dstName
+ if err = backupRepo.CreateRecord(&record); err != nil {
+ global.LOG.Errorf("save backup record failed, err: %v", err)
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ u.HandleRmExpired("LOCAL", "", cronjob, nil)
+ return strings.Join(filePaths, ","), nil
+}
+
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string
localDir, err := loadLocalDir()
diff --git a/frontend/components.d.ts b/frontend/components.d.ts
index b919c0cf1..182ad8cc9 100644
--- a/frontend/components.d.ts
+++ b/frontend/components.d.ts
@@ -5,7 +5,6 @@
declare module 'vue' {
export interface GlobalComponents {
404: typeof import('./src/components/error-message/404.vue')['default']
- AppLayout: typeof import('./src/components/app-layout/index.vue')['default']
AppStatus: typeof import('./src/components/app-status/index.vue')['default']
BackButton: typeof import('./src/components/back-button/index.vue')['default']
Backup: typeof import('./src/components/backup/index.vue')['default']
@@ -13,13 +12,11 @@ declare module 'vue' {
BreadCrumbsItem: typeof import('./src/components/bread-crumbs/bread-crumbs-item.vue')['default']
CardWithHeader: typeof import('./src/components/card-with-header/index.vue')['default']
Codemirror: typeof import('./src/components/codemirror-dialog/codemirror.vue')['default']
- Collapse: typeof import('./src/components/app-layout/menu/components/Collapse.vue')['default']
ComplexTable: typeof import('./src/components/complex-table/index.vue')['default']
ConfirmDialog: typeof import('./src/components/confirm-dialog/index.vue')['default']
ContainerLog: typeof import('./src/components/container-log/index.vue')['default']
DrawerHeader: typeof import('./src/components/drawer-header/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
- ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElButton: typeof import('element-plus/es')['ElButton']
@@ -29,7 +26,6 @@ declare module 'vue' {
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
- ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
@@ -40,7 +36,6 @@ declare module 'vue' {
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
- ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
@@ -48,7 +43,6 @@ declare module 'vue' {
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
- ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
@@ -76,20 +70,16 @@ declare module 'vue' {
Err_ip: typeof import('./src/components/error-message/err_ip.vue')['default']
FileList: typeof import('./src/components/file-list/index.vue')['default']
FileRole: typeof import('./src/components/file-role/index.vue')['default']
- Footer: typeof import('./src/components/app-layout/footer/index.vue')['default']
FormButton: typeof import('./src/components/layout-content/form-button.vue')['default']
Group: typeof import('./src/components/group/index.vue')['default']
InfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
LayoutContent: typeof import('./src/components/layout-content/index.vue')['default']
Line: typeof import('./src/components/v-charts/components/Line.vue')['default']
Loading: typeof import('element-plus/es')['ElLoadingDirective']
- Logo: typeof import('./src/components/app-layout/menu/components/Logo.vue')['default']
- Menu: typeof import('./src/components/app-layout/menu/index.vue')['default']
MsgInfo: typeof import('./src/components/msg-info/index.vue')['default']
Popover: typeof import('element-plus/es')['ElPopoverDirective']
RouterButton: typeof import('./src/components/router-button/index.vue')['default']
Status: typeof import('./src/components/status/index.vue')['default']
- SubItem: typeof import('./src/components/app-layout/menu/components/sub-item.vue')['default']
SvgIcon: typeof import('./src/components/svg-icon/svg-icon.vue')['default']
SystemUpgrade: typeof import('./src/components/system-upgrade/index.vue')['default']
TableSetting: typeof import('./src/components/table-setting/index.vue')['default']
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 65f15caca..36ae8840b 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -656,6 +656,8 @@ const message = {
cleanDataHelper: 'Delete the backup file generated during this task.',
noLogs: 'No task output yet...',
errPath: 'Backup path [{0}] error, cannot download!',
+ cutWebsiteLog: 'cut website log',
+ cutWebsiteLogHelper: 'The cut log files will be backed up to the backup directory of 1Panel',
},
monitor: {
monitor: 'Monitor',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index f1dba76ab..d40f54122 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -663,6 +663,8 @@ const message = {
cleanDataHelper: '删除该任务执行过程中产生的备份文件',
noLogs: '暂无任务输出...',
errPath: '备份路径 [{0}] 错误,无法下载!',
+ cutWebsiteLog: '切割网站日志',
+ cutWebsiteLogHelper: '切割的日志文件会备份到 1Panel 的 backup 目录下',
},
monitor: {
monitor: '监控',
diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue
index 01081bafc..9fe70cc0e 100644
--- a/frontend/src/views/cronjob/operate/index.vue
+++ b/frontend/src/views/cronjob/operate/index.vue
@@ -18,6 +18,7 @@