Browse Source

feat: auto backup #999

Jacky 1 month ago
parent
commit
536aa545e1
60 changed files with 8947 additions and 570 deletions
  1. 1 1
      .vscode/settings.json
  2. 197 0
      api/backup/auto_backup.go
  3. 1 1
      api/backup/backup.go
  4. 1 1
      api/backup/backup_test.go
  5. 1 1
      api/backup/restore.go
  6. 20 0
      api/backup/router.go
  7. 1 6
      api/system/router.go
  8. 0 18
      app/components.d.ts
  9. 121 115
      app/pnpm-lock.yaml
  10. 36 3
      app/src/api/backup.ts
  11. 20 0
      app/src/constants/errors/backup.ts
  12. 383 27
      app/src/language/ar/app.po
  13. 385 23
      app/src/language/de_DE/app.po
  14. 380 20
      app/src/language/en/app.po
  15. 385 23
      app/src/language/es/app.po
  16. 385 27
      app/src/language/fr_FR/app.po
  17. 383 23
      app/src/language/ja_JP/app.po
  18. 383 23
      app/src/language/ko_KR/app.po
  19. 379 18
      app/src/language/messages.pot
  20. 383 23
      app/src/language/pt_PT/app.po
  21. 385 23
      app/src/language/ru_RU/app.po
  22. 383 23
      app/src/language/tr_TR/app.po
  23. 385 23
      app/src/language/uk_UA/app.po
  24. 383 23
      app/src/language/vi_VN/app.po
  25. 383 26
      app/src/language/zh_CN/app.po
  26. 383 26
      app/src/language/zh_TW/app.po
  27. 2 1
      app/src/routes/index.ts
  28. 32 0
      app/src/routes/modules/backup.ts
  29. 0 7
      app/src/routes/modules/system.ts
  30. 1 1
      app/src/version.json
  31. 259 0
      app/src/views/backup/AutoBackup/AutoBackup.vue
  32. 207 0
      app/src/views/backup/AutoBackup/components/CronEditor.vue
  33. 147 0
      app/src/views/backup/AutoBackup/components/StorageConfigEditor.vue
  34. 2 0
      app/src/views/backup/AutoBackup/components/index.ts
  35. 0 0
      app/src/views/backup/components/BackupCreator.vue
  36. 0 2
      app/src/views/backup/components/SystemRestore.vue
  37. 2 2
      app/src/views/backup/index.vue
  38. 3 2
      docs/.vitepress/config/en.ts
  39. 3 2
      docs/.vitepress/config/zh_CN.ts
  40. 3 2
      docs/.vitepress/config/zh_TW.ts
  41. 104 0
      docs/guide/config-backup.md
  42. 104 0
      docs/zh_CN/guide/config-backup.md
  43. 104 0
      docs/zh_TW/guide/config-backup.md
  44. 5 0
      go.mod
  45. 10 0
      go.sum
  46. 539 0
      internal/backup/auto_backup.go
  47. 54 38
      internal/backup/backup.go
  48. 24 0
      internal/backup/errors.go
  49. 241 0
      internal/backup/s3_client.go
  50. 153 0
      internal/backup/s3_client_test.go
  51. 126 15
      internal/backup/utils.go
  52. 146 0
      internal/cron/auto_backup.go
  53. 6 0
      internal/cron/cron.go
  54. 54 0
      model/auto_backup.go
  55. 1 0
      model/model.go
  56. 422 0
      query/auto_backups.gen.go
  57. 8 0
      query/gen.go
  58. 4 1
      router/routers.go
  59. 32 0
      settings/backup.go
  60. 2 0
      settings/settings.go

+ 1 - 1
.vscode/settings.json

@@ -10,7 +10,7 @@
     "i18n-gettext.translatorConfig": {
     "onlyTranslateUntranslatedAndFuzzy": true,
     "batch": {
-      "pageSize": 20
+      "pageSize": 100
     }
   }
 }

+ 197 - 0
api/backup/auto_backup.go

@@ -0,0 +1,197 @@
+package backup
+
+import (
+	"net/http"
+
+	"github.com/0xJacky/Nginx-UI/internal/backup"
+	"github.com/0xJacky/Nginx-UI/internal/cron"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/gin-gonic/gin"
+	"github.com/uozi-tech/cosy"
+	"github.com/uozi-tech/cosy/logger"
+)
+
+// GetAutoBackupList retrieves a paginated list of auto backup configurations.
+// This endpoint supports fuzzy search by backup name and filtering by backup type and enabled status.
+//
+// Query Parameters:
+//   - page: Page number for pagination
+//   - page_size: Number of items per page
+//   - name: Fuzzy search filter for backup name
+//   - backup_type: Filter by backup type (nginx_config/nginx_ui_config/both_config/custom_dir)
+//   - enabled: Filter by enabled status (true/false)
+//
+// Response: Paginated list of auto backup configurations
+func GetAutoBackupList(c *gin.Context) {
+	cosy.Core[model.AutoBackup](c).
+		SetFussy("name").
+		SetEqual("backup_type", "enabled", "storage_type", "last_backup_status").
+		PagingList()
+}
+
+// CreateAutoBackup creates a new auto backup configuration with comprehensive validation.
+// This endpoint validates all required fields, path permissions, and S3 configuration.
+//
+// Request Body: AutoBackup model with required fields
+// Response: Created auto backup configuration
+func CreateAutoBackup(c *gin.Context) {
+	ctx := cosy.Core[model.AutoBackup](c).SetValidRules(gin.H{
+		"name":                 "required",
+		"backup_type":          "required",
+		"storage_type":         "required",
+		"storage_path":         "required",
+		"cron_expression":      "required",
+		"enabled":              "omitempty",
+		"backup_path":          "omitempty",
+		"s3_endpoint":          "omitempty",
+		"s3_access_key_id":     "omitempty",
+		"s3_secret_access_key": "omitempty",
+		"s3_bucket":            "omitempty",
+		"s3_region":            "omitempty",
+	}).BeforeExecuteHook(func(ctx *cosy.Ctx[model.AutoBackup]) {
+		// Validate backup configuration before creation
+		if err := backup.ValidateAutoBackupConfig(&ctx.Model); err != nil {
+			ctx.AbortWithError(err)
+			return
+		}
+	})
+
+	ctx.Create()
+
+	// Register cron job only if the backup is enabled
+	if ctx.Model.Enabled {
+		if err := cron.AddAutoBackupJob(ctx.Model.ID, ctx.Model.CronExpression); err != nil {
+			logger.Errorf("Failed to add auto backup job %d: %v", ctx.Model.ID, err)
+		}
+	}
+}
+
+// GetAutoBackup retrieves a single auto backup configuration by ID.
+//
+// Path Parameters:
+//   - id: Auto backup configuration ID
+//
+// Response: Auto backup configuration details
+func GetAutoBackup(c *gin.Context) {
+	cosy.Core[model.AutoBackup](c).Get()
+}
+
+// ModifyAutoBackup updates an existing auto backup configuration with validation.
+// This endpoint performs the same validation as creation for modified fields.
+//
+// Path Parameters:
+//   - id: Auto backup configuration ID
+//
+// Request Body: Partial AutoBackup model with fields to update
+// Response: Updated auto backup configuration
+func ModifyAutoBackup(c *gin.Context) {
+	ctx := cosy.Core[model.AutoBackup](c).SetValidRules(gin.H{
+		"name":                 "omitempty",
+		"backup_type":          "omitempty",
+		"storage_type":         "omitempty",
+		"storage_path":         "omitempty",
+		"cron_expression":      "omitempty",
+		"backup_path":          "omitempty",
+		"enabled":              "omitempty",
+		"s3_endpoint":          "omitempty",
+		"s3_access_key_id":     "omitempty",
+		"s3_secret_access_key": "omitempty",
+		"s3_bucket":            "omitempty",
+		"s3_region":            "omitempty",
+	}).BeforeExecuteHook(func(ctx *cosy.Ctx[model.AutoBackup]) {
+		// Validate backup configuration before modification
+		if err := backup.ValidateAutoBackupConfig(&ctx.Model); err != nil {
+			ctx.AbortWithError(err)
+			return
+		}
+	})
+
+	ctx.Modify()
+
+	// Update cron job based on enabled status
+	if ctx.Model.Enabled {
+		if err := cron.UpdateAutoBackupJob(ctx.Model.ID, ctx.Model.CronExpression); err != nil {
+			logger.Errorf("Failed to update auto backup job %d: %v", ctx.Model.ID, err)
+		}
+	} else {
+		if err := cron.RemoveAutoBackupJob(ctx.Model.ID); err != nil {
+			logger.Errorf("Failed to remove auto backup job %d: %v", ctx.Model.ID, err)
+		}
+	}
+}
+
+// DestroyAutoBackup deletes an auto backup configuration and removes its cron job.
+// This endpoint ensures proper cleanup of both database records and scheduled tasks.
+//
+// Path Parameters:
+//   - id: Auto backup configuration ID
+//
+// Response: Success confirmation
+func DestroyAutoBackup(c *gin.Context) {
+	cosy.Core[model.AutoBackup](c).BeforeExecuteHook(func(ctx *cosy.Ctx[model.AutoBackup]) {
+		// Remove cron job before deleting the backup task
+		if err := cron.RemoveAutoBackupJob(ctx.Model.ID); err != nil {
+			logger.Errorf("Failed to remove auto backup job %d: %v", ctx.Model.ID, err)
+		}
+	}).Destroy()
+}
+
+// TestS3Connection tests the S3 connection for auto backup configuration.
+// This endpoint allows users to verify their S3 settings before saving the configuration.
+//
+// Request Body: AutoBackup model with S3 configuration
+// Response: Success confirmation or error details
+func TestS3Connection(c *gin.Context) {
+	var autoBackup model.AutoBackup
+	if !cosy.BindAndValid(c, &autoBackup) {
+		return
+	}
+
+	// Validate S3 configuration
+	if err := backup.ValidateS3Config(&autoBackup); err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+
+	// Test S3 connection
+	if err := backup.TestS3ConnectionForConfig(&autoBackup); err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{"message": "S3 connection test successful"})
+}
+
+// RestoreAutoBackup restores a soft-deleted auto backup configuration.
+// This endpoint restores the backup configuration and re-registers the cron job if enabled.
+//
+// Path Parameters:
+//   - id: Auto backup configuration ID to restore
+//
+// Response: Success confirmation
+func RestoreAutoBackup(c *gin.Context) {
+	var autoBackup model.AutoBackup
+	if err := c.ShouldBindUri(&autoBackup); err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+
+	// Restore the backup configuration
+	if err := backup.RestoreAutoBackup(autoBackup.ID); err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+
+	// Get the restored backup configuration to check if it's enabled
+	restoredBackup, err := backup.GetAutoBackupByID(autoBackup.ID)
+	if err != nil {
+		logger.Errorf("Failed to get restored auto backup %d: %v", autoBackup.ID, err)
+	} else if restoredBackup.Enabled {
+		// Register cron job if the backup is enabled
+		if err := cron.AddAutoBackupJob(restoredBackup.ID, restoredBackup.CronExpression); err != nil {
+			logger.Errorf("Failed to add auto backup job %d after restore: %v", restoredBackup.ID, err)
+		}
+	}
+
+	c.JSON(http.StatusOK, gin.H{"message": "Auto backup restored successfully"})
+}

+ 1 - 1
api/system/backup.go → api/backup/backup.go

@@ -1,4 +1,4 @@
-package system
+package backup
 
 import (
 	"bytes"

+ 1 - 1
api/system/backup_test.go → api/backup/backup_test.go

@@ -1,4 +1,4 @@
-package system
+package backup
 
 import (
 	"bytes"

+ 1 - 1
api/system/restore.go → api/backup/restore.go

@@ -1,4 +1,4 @@
-package system
+package backup
 
 import (
 	"encoding/base64"

+ 20 - 0
api/backup/router.go

@@ -0,0 +1,20 @@
+package backup
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+func InitRouter(r *gin.RouterGroup) {
+	r.POST("/backup", CreateBackup)
+	r.POST("/restore", RestoreBackup)
+}
+
+func InitAutoBackupRouter(r *gin.RouterGroup) {
+	r.GET("/auto_backup", GetAutoBackupList)
+	r.POST("/auto_backup", CreateAutoBackup)
+	r.GET("/auto_backup/:id", GetAutoBackup)
+	r.PUT("/auto_backup/:id", ModifyAutoBackup)
+	r.DELETE("/auto_backup/:id", DestroyAutoBackup)
+	r.POST("/auto_backup/:id/restore", RestoreAutoBackup)
+	r.POST("/auto_backup/test_s3", TestS3Connection)
+}

+ 1 - 6
api/system/router.go

@@ -23,7 +23,6 @@ func InitPrivateRouter(r *gin.RouterGroup) {
 	r.GET("upgrade/release", GetRelease)
 	r.GET("upgrade/current", GetCurrentVersion)
 
-	r.GET("system/backup", CreateBackup)
 	r.GET("system/processing", GetProcessingStatus)
 }
 
@@ -36,11 +35,7 @@ func InitSelfCheckRouter(r *gin.RouterGroup) {
 }
 
 func InitBackupRestoreRouter(r *gin.RouterGroup) {
-	r.POST("system/backup/restore",
-		authIfInstalled,
-		middleware.Proxy(),
-		middleware.EncryptedForm(),
-		RestoreBackup)
+	// Backup and restore routes moved to api/backup package
 }
 
 func InitWebSocketRouter(r *gin.RouterGroup) {

+ 0 - 18
app/components.d.ts

@@ -23,8 +23,6 @@ declare module 'vue' {
     ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
     AComment: typeof import('ant-design-vue/es')['Comment']
     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
-    ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
-    ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']
     ADivider: typeof import('ant-design-vue/es')['Divider']
     ADrawer: typeof import('ant-design-vue/es')['Drawer']
     ADropdown: typeof import('ant-design-vue/es')['Dropdown']
@@ -46,16 +44,12 @@ declare module 'vue' {
     AMenu: typeof import('ant-design-vue/es')['Menu']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
     AModal: typeof import('ant-design-vue/es')['Modal']
-    APagination: typeof import('ant-design-vue/es')['Pagination']
     APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
     APopover: typeof import('ant-design-vue/es')['Popover']
     AProgress: typeof import('ant-design-vue/es')['Progress']
     AQrcode: typeof import('ant-design-vue/es')['QRCode']
-    ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
-    ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
     AResult: typeof import('ant-design-vue/es')['Result']
     ARow: typeof import('ant-design-vue/es')['Row']
-    ASegmented: typeof import('ant-design-vue/es')['Segmented']
     ASelect: typeof import('ant-design-vue/es')['Select']
     ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
     ASpace: typeof import('ant-design-vue/es')['Space']
@@ -111,18 +105,6 @@ declare module 'vue' {
     SelfCheckSelfCheckHeaderBanner: typeof import('./src/components/SelfCheck/SelfCheckHeaderBanner.vue')['default']
     SensitiveStringSensitiveString: typeof import('./src/components/SensitiveString/SensitiveString.vue')['default']
     SetLanguageSetLanguage: typeof import('./src/components/SetLanguage/SetLanguage.vue')['default']
-    StdDesignStdDataDisplayStdBatchEdit: typeof import('./src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue')['default']
-    StdDesignStdDataDisplayStdBulkActions: typeof import('./src/components/StdDesign/StdDataDisplay/StdBulkActions.vue')['default']
-    StdDesignStdDataDisplayStdCurd: typeof import('./src/components/StdDesign/StdDataDisplay/StdCurd.vue')['default']
-    StdDesignStdDataDisplayStdCurdDetail: typeof import('./src/components/StdDesign/StdDataDisplay/StdCurdDetail.vue')['default']
-    StdDesignStdDataDisplayStdPagination: typeof import('./src/components/StdDesign/StdDataDisplay/StdPagination.vue')['default']
-    StdDesignStdDataDisplayStdTable: typeof import('./src/components/StdDesign/StdDataDisplay/StdTable.vue')['default']
-    StdDesignStdDataEntryComponentsStdPassword: typeof import('./src/components/StdDesign/StdDataEntry/components/StdPassword.vue')['default']
-    StdDesignStdDataEntryComponentsStdSelect: typeof import('./src/components/StdDesign/StdDataEntry/components/StdSelect.vue')['default']
-    StdDesignStdDataEntryComponentsStdSelector: typeof import('./src/components/StdDesign/StdDataEntry/components/StdSelector.vue')['default']
-    StdDesignStdDataEntryStdDataEntry: typeof import('./src/components/StdDesign/StdDataEntry/StdDataEntry.vue')['default']
-    StdDesignStdDataEntryStdFormItem: typeof import('./src/components/StdDesign/StdDataEntry/StdFormItem.vue')['default']
-    StdDesignStdDetailStdDetail: typeof import('./src/components/StdDesign/StdDetail/StdDetail.vue')['default']
     SwitchAppearanceIconsVPIconMoon: typeof import('./src/components/SwitchAppearance/icons/VPIconMoon.vue')['default']
     SwitchAppearanceIconsVPIconSun: typeof import('./src/components/SwitchAppearance/icons/VPIconSun.vue')['default']
     SwitchAppearanceSwitchAppearance: typeof import('./src/components/SwitchAppearance/SwitchAppearance.vue')['default']

+ 121 - 115
app/pnpm-lock.yaml

@@ -847,16 +847,16 @@ packages:
     peerDependencies:
       vue: '>=3'
 
-  '@intlify/core-base@11.1.3':
-    resolution: {integrity: sha512-cMuHunYO7LE80azTitcvEbs1KJmtd6g7I5pxlApV3Jo547zdO3h31/0uXpqHc+Y3RKt1wo2y68RGSx77Z1klyA==}
+  '@intlify/core-base@11.1.4':
+    resolution: {integrity: sha512-VNIanL84HNBNAoJjPA2V8EykT5NtgNDquO2MsDQcSheo7EcCt4uvH14IHBEDKVoL6k38NNICLuRhtKOKqW2ylA==}
     engines: {node: '>= 16'}
 
-  '@intlify/message-compiler@11.1.3':
-    resolution: {integrity: sha512-7rbqqpo2f5+tIcwZTAG/Ooy9C8NDVwfDkvSeDPWUPQW+Dyzfw2o9H103N5lKBxO7wxX9dgCDjQ8Umz73uYw3hw==}
+  '@intlify/message-compiler@11.1.4':
+    resolution: {integrity: sha512-fQWJwTOBFNFGNr4I5k629hQxTGEKsDWhhTzr6Y4CN4OXJw/dLB/VbbQm5jlylqnv44RBZN5GSD+d1nWpNcAR5A==}
     engines: {node: '>= 16'}
 
-  '@intlify/shared@11.1.3':
-    resolution: {integrity: sha512-pTFBgqa/99JRA2H1qfyqv97MKWJrYngXBA/I0elZcYxvJgcCw3mApAoPW3mJ7vx3j+Ti0FyKUFZ4hWxdjKaxvA==}
+  '@intlify/shared@11.1.4':
+    resolution: {integrity: sha512-zOW2L5+QnWRQgM/7WNSPxa6E0F3wR2/KEQV7P4s4AXzxzmg0MuzLNiixvkRJU5h0Xb3DnHic6zybKva28kabDw==}
     engines: {node: '>= 16'}
 
   '@isaacs/cliui@8.0.2':
@@ -918,103 +918,103 @@ packages:
   '@rolldown/pluginutils@1.0.0-beta.9':
     resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==}
 
-  '@rollup/rollup-android-arm-eabi@4.41.0':
-    resolution: {integrity: sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==}
+  '@rollup/rollup-android-arm-eabi@4.41.1':
+    resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==}
     cpu: [arm]
     os: [android]
 
-  '@rollup/rollup-android-arm64@4.41.0':
-    resolution: {integrity: sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==}
+  '@rollup/rollup-android-arm64@4.41.1':
+    resolution: {integrity: sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==}
     cpu: [arm64]
     os: [android]
 
-  '@rollup/rollup-darwin-arm64@4.41.0':
-    resolution: {integrity: sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==}
+  '@rollup/rollup-darwin-arm64@4.41.1':
+    resolution: {integrity: sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==}
     cpu: [arm64]
     os: [darwin]
 
-  '@rollup/rollup-darwin-x64@4.41.0':
-    resolution: {integrity: sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==}
+  '@rollup/rollup-darwin-x64@4.41.1':
+    resolution: {integrity: sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==}
     cpu: [x64]
     os: [darwin]
 
-  '@rollup/rollup-freebsd-arm64@4.41.0':
-    resolution: {integrity: sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==}
+  '@rollup/rollup-freebsd-arm64@4.41.1':
+    resolution: {integrity: sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==}
     cpu: [arm64]
     os: [freebsd]
 
-  '@rollup/rollup-freebsd-x64@4.41.0':
-    resolution: {integrity: sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==}
+  '@rollup/rollup-freebsd-x64@4.41.1':
+    resolution: {integrity: sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==}
     cpu: [x64]
     os: [freebsd]
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.41.0':
-    resolution: {integrity: sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==}
+  '@rollup/rollup-linux-arm-gnueabihf@4.41.1':
+    resolution: {integrity: sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm-musleabihf@4.41.0':
-    resolution: {integrity: sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==}
+  '@rollup/rollup-linux-arm-musleabihf@4.41.1':
+    resolution: {integrity: sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==}
     cpu: [arm]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-gnu@4.41.0':
-    resolution: {integrity: sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==}
+  '@rollup/rollup-linux-arm64-gnu@4.41.1':
+    resolution: {integrity: sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-arm64-musl@4.41.0':
-    resolution: {integrity: sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==}
+  '@rollup/rollup-linux-arm64-musl@4.41.1':
+    resolution: {integrity: sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==}
     cpu: [arm64]
     os: [linux]
 
-  '@rollup/rollup-linux-loongarch64-gnu@4.41.0':
-    resolution: {integrity: sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==}
+  '@rollup/rollup-linux-loongarch64-gnu@4.41.1':
+    resolution: {integrity: sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==}
     cpu: [loong64]
     os: [linux]
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.41.0':
-    resolution: {integrity: sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==}
+  '@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
+    resolution: {integrity: sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==}
     cpu: [ppc64]
     os: [linux]
 
-  '@rollup/rollup-linux-riscv64-gnu@4.41.0':
-    resolution: {integrity: sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==}
+  '@rollup/rollup-linux-riscv64-gnu@4.41.1':
+    resolution: {integrity: sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==}
     cpu: [riscv64]
     os: [linux]
 
-  '@rollup/rollup-linux-riscv64-musl@4.41.0':
-    resolution: {integrity: sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==}
+  '@rollup/rollup-linux-riscv64-musl@4.41.1':
+    resolution: {integrity: sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==}
     cpu: [riscv64]
     os: [linux]
 
-  '@rollup/rollup-linux-s390x-gnu@4.41.0':
-    resolution: {integrity: sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==}
+  '@rollup/rollup-linux-s390x-gnu@4.41.1':
+    resolution: {integrity: sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==}
     cpu: [s390x]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-gnu@4.41.0':
-    resolution: {integrity: sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==}
+  '@rollup/rollup-linux-x64-gnu@4.41.1':
+    resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-linux-x64-musl@4.41.0':
-    resolution: {integrity: sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==}
+  '@rollup/rollup-linux-x64-musl@4.41.1':
+    resolution: {integrity: sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==}
     cpu: [x64]
     os: [linux]
 
-  '@rollup/rollup-win32-arm64-msvc@4.41.0':
-    resolution: {integrity: sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==}
+  '@rollup/rollup-win32-arm64-msvc@4.41.1':
+    resolution: {integrity: sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==}
     cpu: [arm64]
     os: [win32]
 
-  '@rollup/rollup-win32-ia32-msvc@4.41.0':
-    resolution: {integrity: sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==}
+  '@rollup/rollup-win32-ia32-msvc@4.41.1':
+    resolution: {integrity: sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==}
     cpu: [ia32]
     os: [win32]
 
-  '@rollup/rollup-win32-x64-msvc@4.41.0':
-    resolution: {integrity: sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==}
+  '@rollup/rollup-win32-x64-msvc@4.41.1':
+    resolution: {integrity: sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==}
     cpu: [x64]
     os: [win32]
 
@@ -1024,8 +1024,8 @@ packages:
   '@simplewebauthn/browser@13.1.0':
     resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==}
 
-  '@stylistic/eslint-plugin@4.2.0':
-    resolution: {integrity: sha512-8hXezgz7jexGHdo5WN6JBEIPHCSFyyU4vgbxevu4YLVS5vl+sxqAAGyXSzfNDyR6xMNSH5H1x67nsXcYMOHtZA==}
+  '@stylistic/eslint-plugin@4.4.0':
+    resolution: {integrity: sha512-bIh/d9X+OQLCAMdhHtps+frvyjvAM4B1YlSJzcEEhl7wXLIqPar3ngn9DrHhkBOrTA/z9J0bUMtctAspe0dxdQ==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: '>=9.0.0'
@@ -2147,6 +2147,10 @@ packages:
   eslint-flat-config-utils@2.1.0:
     resolution: {integrity: sha512-6fjOJ9tS0k28ketkUcQ+kKptB4dBZY2VijMZ9rGn8Cwnn1SH0cZBoPXT8AHBFHxmHcLFQK9zbELDinZ2Mr1rng==}
 
+  eslint-import-context@0.1.3:
+    resolution: {integrity: sha512-dmdfEU9i8EgxV7jR9IAeiC6mp22wNfclhs33D8zctPYiclQhq61idm/Q4VJv00SPvqLWTokvN5lDyNkuQZYzUg==}
+    engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
   eslint-import-resolver-node@0.3.9:
     resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
 
@@ -2171,8 +2175,8 @@ packages:
     peerDependencies:
       eslint: '*'
 
-  eslint-plugin-command@3.2.0:
-    resolution: {integrity: sha512-PSDOB9k7Wd57pp4HD/l3C1D93pKX8/wQo0kWDI4q6/UpgrfMTyNsavklipgiZqbXl1+VBABY1buCcQE5LDpg5g==}
+  eslint-plugin-command@3.2.1:
+    resolution: {integrity: sha512-PcpzWe8dvAPaBobxE9zgz1w94fO4JYvzciDzw6thlUb9Uqf5e2/gJz97itOGxvdq+mFeudi71m1OGFgvWmb93w==}
     peerDependencies:
       eslint: '*'
 
@@ -2182,8 +2186,8 @@ packages:
     peerDependencies:
       eslint: '>=8'
 
-  eslint-plugin-import-x@4.12.2:
-    resolution: {integrity: sha512-0jVUgJQipbs0yUfLe7LwYD6p8rIGqCysWZdyJFgkPzDyJgiKpuCaXlywKUAWgJ6u1nLpfrdt21B60OUkupyBrQ==}
+  eslint-plugin-import-x@4.13.0:
+    resolution: {integrity: sha512-YtbaFHEQYGvR9zPuI525UhwPKEK0sXAvPyHKBUZYadJeIhD6UIuQE60Fje4q5nP17fkhzxjECrFV20nSdLrQtg==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
@@ -3457,8 +3461,8 @@ packages:
   rfdc@1.4.1:
     resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
 
-  rollup@4.41.0:
-    resolution: {integrity: sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==}
+  rollup@4.41.1:
+    resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
 
@@ -3971,8 +3975,8 @@ packages:
     peerDependencies:
       vue: ^3.4.37
 
-  vue-i18n@11.1.3:
-    resolution: {integrity: sha512-Pcylh9z9S5+CJAqgbRZ3EKxFIBIrtY5YUppU722GIT65+Nukm0TCqiQegZnNLCZkXGthxe0cpqj0AoM51H+6Gw==}
+  vue-i18n@11.1.4:
+    resolution: {integrity: sha512-0B2Q4rTSzQigfIQnsgNMgWOekouT2lr3hiKG3k7q3fQykr968BRdIUDnIvHisq/f1FPKbBznHpvAyGg78eDAyg==}
     engines: {node: '>= 16'}
     peerDependencies:
       vue: ^3.0.0
@@ -4155,7 +4159,7 @@ snapshots:
       '@clack/prompts': 0.10.1
       '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.27.0(jiti@2.4.2))
       '@eslint/markdown': 6.4.0
-      '@stylistic/eslint-plugin': 4.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
+      '@stylistic/eslint-plugin': 4.4.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       '@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       '@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       '@vitest/eslint-plugin': 1.2.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
@@ -4166,8 +4170,8 @@ snapshots:
       eslint-flat-config-utils: 2.1.0
       eslint-merge-processors: 2.0.0(eslint@9.27.0(jiti@2.4.2))
       eslint-plugin-antfu: 3.1.1(eslint@9.27.0(jiti@2.4.2))
-      eslint-plugin-command: 3.2.0(eslint@9.27.0(jiti@2.4.2))
-      eslint-plugin-import-x: 4.12.2(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
+      eslint-plugin-command: 3.2.1(eslint@9.27.0(jiti@2.4.2))
+      eslint-plugin-import-x: 4.13.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       eslint-plugin-jsdoc: 50.6.17(eslint@9.27.0(jiti@2.4.2))
       eslint-plugin-jsonc: 2.20.1(eslint@9.27.0(jiti@2.4.2))
       eslint-plugin-n: 17.18.0(eslint@9.27.0(jiti@2.4.2))
@@ -4696,17 +4700,17 @@ snapshots:
       '@iconify/types': 2.0.0
       vue: 3.5.14(typescript@5.8.3)
 
-  '@intlify/core-base@11.1.3':
+  '@intlify/core-base@11.1.4':
     dependencies:
-      '@intlify/message-compiler': 11.1.3
-      '@intlify/shared': 11.1.3
+      '@intlify/message-compiler': 11.1.4
+      '@intlify/shared': 11.1.4
 
-  '@intlify/message-compiler@11.1.3':
+  '@intlify/message-compiler@11.1.4':
     dependencies:
-      '@intlify/shared': 11.1.3
+      '@intlify/shared': 11.1.4
       source-map-js: 1.2.1
 
-  '@intlify/shared@11.1.3': {}
+  '@intlify/shared@11.1.4': {}
 
   '@isaacs/cliui@8.0.2':
     dependencies:
@@ -4793,64 +4797,64 @@ snapshots:
 
   '@rolldown/pluginutils@1.0.0-beta.9': {}
 
-  '@rollup/rollup-android-arm-eabi@4.41.0':
+  '@rollup/rollup-android-arm-eabi@4.41.1':
     optional: true
 
-  '@rollup/rollup-android-arm64@4.41.0':
+  '@rollup/rollup-android-arm64@4.41.1':
     optional: true
 
-  '@rollup/rollup-darwin-arm64@4.41.0':
+  '@rollup/rollup-darwin-arm64@4.41.1':
     optional: true
 
-  '@rollup/rollup-darwin-x64@4.41.0':
+  '@rollup/rollup-darwin-x64@4.41.1':
     optional: true
 
-  '@rollup/rollup-freebsd-arm64@4.41.0':
+  '@rollup/rollup-freebsd-arm64@4.41.1':
     optional: true
 
-  '@rollup/rollup-freebsd-x64@4.41.0':
+  '@rollup/rollup-freebsd-x64@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.41.0':
+  '@rollup/rollup-linux-arm-gnueabihf@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-arm-musleabihf@4.41.0':
+  '@rollup/rollup-linux-arm-musleabihf@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-arm64-gnu@4.41.0':
+  '@rollup/rollup-linux-arm64-gnu@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-arm64-musl@4.41.0':
+  '@rollup/rollup-linux-arm64-musl@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-loongarch64-gnu@4.41.0':
+  '@rollup/rollup-linux-loongarch64-gnu@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.41.0':
+  '@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-riscv64-gnu@4.41.0':
+  '@rollup/rollup-linux-riscv64-gnu@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-riscv64-musl@4.41.0':
+  '@rollup/rollup-linux-riscv64-musl@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-s390x-gnu@4.41.0':
+  '@rollup/rollup-linux-s390x-gnu@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-x64-gnu@4.41.0':
+  '@rollup/rollup-linux-x64-gnu@4.41.1':
     optional: true
 
-  '@rollup/rollup-linux-x64-musl@4.41.0':
+  '@rollup/rollup-linux-x64-musl@4.41.1':
     optional: true
 
-  '@rollup/rollup-win32-arm64-msvc@4.41.0':
+  '@rollup/rollup-win32-arm64-msvc@4.41.1':
     optional: true
 
-  '@rollup/rollup-win32-ia32-msvc@4.41.0':
+  '@rollup/rollup-win32-ia32-msvc@4.41.1':
     optional: true
 
-  '@rollup/rollup-win32-x64-msvc@4.41.0':
+  '@rollup/rollup-win32-x64-msvc@4.41.1':
     optional: true
 
   '@simonwep/pickr@1.8.2':
@@ -4860,7 +4864,7 @@ snapshots:
 
   '@simplewebauthn/browser@13.1.0': {}
 
-  '@stylistic/eslint-plugin@4.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)':
+  '@stylistic/eslint-plugin@4.4.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)':
     dependencies:
       '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       eslint: 9.27.0(jiti@2.4.2)
@@ -5240,7 +5244,7 @@ snapshots:
       scroll-into-view-if-needed: 3.1.0
       sortablejs: 1.15.6
       vue: 3.5.14(typescript@5.8.3)
-      vue-i18n: 11.1.3(vue@3.5.14(typescript@5.8.3))
+      vue-i18n: 11.1.4(vue@3.5.14(typescript@5.8.3))
       vue-router: 4.5.1(vue@3.5.14(typescript@5.8.3))
       vue-types: 6.0.0(vue@3.5.14(typescript@5.8.3))
       xlsx: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
@@ -6180,6 +6184,8 @@ snapshots:
     dependencies:
       pathe: 2.0.3
 
+  eslint-import-context@0.1.3: {}
+
   eslint-import-resolver-node@0.3.9:
     dependencies:
       debug: 3.2.7
@@ -6202,7 +6208,7 @@ snapshots:
     dependencies:
       eslint: 9.27.0(jiti@2.4.2)
 
-  eslint-plugin-command@3.2.0(eslint@9.27.0(jiti@2.4.2)):
+  eslint-plugin-command@3.2.1(eslint@9.27.0(jiti@2.4.2)):
     dependencies:
       '@es-joy/jsdoccomment': 0.50.2
       eslint: 9.27.0(jiti@2.4.2)
@@ -6214,14 +6220,14 @@ snapshots:
       eslint: 9.27.0(jiti@2.4.2)
       eslint-compat-utils: 0.5.1(eslint@9.27.0(jiti@2.4.2))
 
-  eslint-plugin-import-x@4.12.2(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3):
+  eslint-plugin-import-x@4.13.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3):
     dependencies:
       '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       comment-parser: 1.4.1
       debug: 4.4.1
       eslint: 9.27.0(jiti@2.4.2)
+      eslint-import-context: 0.1.3
       eslint-import-resolver-node: 0.3.9
-      get-tsconfig: 4.10.1
       is-glob: 4.0.3
       minimatch: 10.0.1
       semver: 7.7.2
@@ -7750,30 +7756,30 @@ snapshots:
 
   rfdc@1.4.1: {}
 
-  rollup@4.41.0:
+  rollup@4.41.1:
     dependencies:
       '@types/estree': 1.0.7
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.41.0
-      '@rollup/rollup-android-arm64': 4.41.0
-      '@rollup/rollup-darwin-arm64': 4.41.0
-      '@rollup/rollup-darwin-x64': 4.41.0
-      '@rollup/rollup-freebsd-arm64': 4.41.0
-      '@rollup/rollup-freebsd-x64': 4.41.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.41.0
-      '@rollup/rollup-linux-arm-musleabihf': 4.41.0
-      '@rollup/rollup-linux-arm64-gnu': 4.41.0
-      '@rollup/rollup-linux-arm64-musl': 4.41.0
-      '@rollup/rollup-linux-loongarch64-gnu': 4.41.0
-      '@rollup/rollup-linux-powerpc64le-gnu': 4.41.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.41.0
-      '@rollup/rollup-linux-riscv64-musl': 4.41.0
-      '@rollup/rollup-linux-s390x-gnu': 4.41.0
-      '@rollup/rollup-linux-x64-gnu': 4.41.0
-      '@rollup/rollup-linux-x64-musl': 4.41.0
-      '@rollup/rollup-win32-arm64-msvc': 4.41.0
-      '@rollup/rollup-win32-ia32-msvc': 4.41.0
-      '@rollup/rollup-win32-x64-msvc': 4.41.0
+      '@rollup/rollup-android-arm-eabi': 4.41.1
+      '@rollup/rollup-android-arm64': 4.41.1
+      '@rollup/rollup-darwin-arm64': 4.41.1
+      '@rollup/rollup-darwin-x64': 4.41.1
+      '@rollup/rollup-freebsd-arm64': 4.41.1
+      '@rollup/rollup-freebsd-x64': 4.41.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.41.1
+      '@rollup/rollup-linux-arm-musleabihf': 4.41.1
+      '@rollup/rollup-linux-arm64-gnu': 4.41.1
+      '@rollup/rollup-linux-arm64-musl': 4.41.1
+      '@rollup/rollup-linux-loongarch64-gnu': 4.41.1
+      '@rollup/rollup-linux-powerpc64le-gnu': 4.41.1
+      '@rollup/rollup-linux-riscv64-gnu': 4.41.1
+      '@rollup/rollup-linux-riscv64-musl': 4.41.1
+      '@rollup/rollup-linux-s390x-gnu': 4.41.1
+      '@rollup/rollup-linux-x64-gnu': 4.41.1
+      '@rollup/rollup-linux-x64-musl': 4.41.1
+      '@rollup/rollup-win32-arm64-msvc': 4.41.1
+      '@rollup/rollup-win32-ia32-msvc': 4.41.1
+      '@rollup/rollup-win32-x64-msvc': 4.41.1
       fsevents: 2.3.3
 
   run-applescript@7.0.0: {}
@@ -8368,7 +8374,7 @@ snapshots:
       fdir: 6.4.4(picomatch@4.0.2)
       picomatch: 4.0.2
       postcss: 8.5.3
-      rollup: 4.41.0
+      rollup: 4.41.1
       tinyglobby: 0.2.13
     optionalDependencies:
       '@types/node': 22.15.21
@@ -8402,10 +8408,10 @@ snapshots:
     dependencies:
       vue: 3.5.14(typescript@5.8.3)
 
-  vue-i18n@11.1.3(vue@3.5.14(typescript@5.8.3)):
+  vue-i18n@11.1.4(vue@3.5.14(typescript@5.8.3)):
     dependencies:
-      '@intlify/core-base': 11.1.3
-      '@intlify/shared': 11.1.3
+      '@intlify/core-base': 11.1.4
+      '@intlify/shared': 11.1.4
       '@vue/devtools-api': 6.6.4
       vue: 3.5.14(typescript@5.8.3)
 

+ 36 - 3
app/src/api/backup.ts

@@ -1,4 +1,5 @@
-import { http } from '@uozi-admin/request'
+import type { ModelBase } from '@/api/curd'
+import { http, useCurdApi } from '@uozi-admin/request'
 
 /**
  * Interface for restore backup response
@@ -21,13 +22,34 @@ export interface RestoreOptions {
   verify_hash: boolean
 }
 
+/**
+ * Interface for auto backup configuration
+ */
+export interface AutoBackup extends ModelBase {
+  name: string
+  backup_type: 'nginx_config' | 'nginx_ui_config' | 'both_config' | 'custom_dir'
+  storage_type: 'local' | 's3'
+  backup_path?: string
+  storage_path: string
+  cron_expression: string
+  enabled: boolean
+  last_backup_time?: string
+  last_backup_status: 'pending' | 'success' | 'failed'
+  last_backup_error?: string
+  s3_endpoint?: string
+  s3_access_key_id?: string
+  s3_secret_access_key?: string
+  s3_bucket?: string
+  s3_region?: string
+}
+
 const backup = {
   /**
    * Create and download a backup of nginx-ui and nginx configurations
    * Use http module with returnFullResponse option to access headers
    */
   createBackup() {
-    return http.get('/system/backup', {
+    return http.get('/backup', {
       responseType: 'blob',
       returnFullResponse: true,
     })
@@ -45,7 +67,7 @@ const backup = {
     formData.append('restore_nginx_ui', options.restore_nginx_ui.toString())
     formData.append('verify_hash', options.verify_hash.toString())
 
-    return http.post('/system/backup/restore', formData, {
+    return http.post('/restore', formData, {
       headers: {
         'Content-Type': 'multipart/form-data;charset=UTF-8',
       },
@@ -54,4 +76,15 @@ const backup = {
   },
 }
 
+/**
+ * Test S3 connection for auto backup configuration
+ * @param config AutoBackup configuration with S3 settings
+ */
+export function testS3Connection(config: AutoBackup) {
+  return http.post('/auto_backup/test_s3', config)
+}
+
+// Auto backup CRUD API
+export const autoBackup = useCurdApi<AutoBackup>('/auto_backup')
+
 export default backup

+ 20 - 0
app/src/constants/errors/backup.ts

@@ -57,4 +57,24 @@ export default {
   4802: () => $gettext('Failed to calculate Nginx UI hash: {0}'),
   4803: () => $gettext('Failed to calculate Nginx hash: {0}'),
   4804: () => $gettext('Hash verification failed: file integrity compromised'),
+  4901: () => $gettext('Backup path not in granted access paths: {0}'),
+  4902: () => $gettext('Storage path not in granted access paths: {0}'),
+  4903: () => $gettext('Backup path is required for custom directory backup'),
+  4904: () => $gettext('S3 configuration is incomplete: missing {0}'),
+  4905: () => $gettext('Unsupported backup type: {0}'),
+  4906: () => $gettext('Failed to create backup directory: {0}'),
+  4907: () => $gettext('Failed to write backup file: {0}'),
+  4908: () => $gettext('Failed to write security key file: {0}'),
+  4909: () => $gettext('S3 upload failed: {0}'),
+  4920: () => $gettext('S3 connection test failed: {0}'),
+  4921: () => $gettext('S3 bucket access denied: {0}'),
+  4922: () => $gettext('S3 credentials are invalid: {0}'),
+  4923: () => $gettext('S3 endpoint is invalid: {0}'),
+  4910: () => $gettext('Invalid path: {0}'),
+  4911: () => $gettext('Path not in granted access paths: {0}'),
+  4912: () => $gettext('Backup path does not exist: {0}'),
+  4913: () => $gettext('Cannot access backup path {0}: {1}'),
+  4914: () => $gettext('Backup path is not a directory: {0}'),
+  4915: () => $gettext('Failed to create storage directory {0}: {1}'),
+  4916: () => $gettext('Cannot access storage path {0}: {1}'),
 }

+ 383 - 27
app/src/language/ar/app.po

@@ -107,7 +107,7 @@ msgstr "المصادقة الثنائية"
 msgid "2FA Settings"
 msgstr "إعدادات المصادقة الثنائية"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "عن"
 
@@ -133,6 +133,7 @@ msgstr "مستخدم ACME"
 msgid "Action"
 msgstr "إجراء"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -345,6 +346,11 @@ msgstr "آلي"
 msgid "auto = CPU cores"
 msgstr "Auto = CPU Cores"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "النسخ الاحتياطي التلقائي"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "التحديث التلقائي"
@@ -386,7 +392,7 @@ msgstr "العودة إلى الصفحة الرئيسية"
 msgid "Back to List"
 msgstr "العودة إلى القائمة"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "النسخ الاحتياطي"
 
@@ -398,10 +404,38 @@ msgstr "فشل التحقق من سلامة ملف النسخ الاحتياطي
 msgid "Backup file not found: {0}"
 msgstr "لم يتم العثور على ملف النسخ الاحتياطي: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "تم تنزيل النسخة الاحتياطية بنجاح"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "مسار النسخ الاحتياطي"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "مسار النسخ الاحتياطي غير موجود: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "مسار النسخ الاحتياطي ليس مجلدًا: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "مسار النسخ الاحتياطي مطلوب لنسخ الدليل المخصص"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "مسار النسخ الاحتياطي غير موجود في مسارات الوصول الممنوحة: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "جدول النسخ الاحتياطي"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "نوع النسخ الاحتياطي"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "دقائق حد الحظر"
@@ -457,6 +491,14 @@ msgstr "فيما يلي العناصر المحددة التي تريد تعدي
 msgid "Block is nil"
 msgstr "الكتلة فارغة"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "كلا التكوينين"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "كل من تكوين Nginx وتكوين واجهة Nginx"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "بناء مع"
@@ -528,6 +570,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "إلغاء"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "لا يمكن الوصول إلى مسار النسخ الاحتياطي {0}: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "لا يمكن الوصول إلى مسار التخزين {0}: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "لا يمكن تغيير كلمة مرور المستخدم الأولي في وضع التجربة"
@@ -974,12 +1024,12 @@ msgstr "محتوى"
 msgid "Copied"
 msgstr "تم النسخ"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "تم النسخ!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "نسخ"
 
@@ -1015,7 +1065,7 @@ msgstr "إنشاء"
 msgid "Create Another"
 msgstr "إنشاء آخر"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "إنشاء نسخة احتياطية"
 
@@ -1027,7 +1077,7 @@ msgstr "إنشاء ملف"
 msgid "Create Folder"
 msgstr "إنشاء مجلد"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1035,6 +1085,7 @@ msgstr ""
 "إنشاء نسخ احتياطية للنظام تتضمن تكوين Nginx وإعدادات واجهة مستخدم Nginx. "
 "سيتم تنزيل ملفات النسخ الاحتياطي تلقائيًا إلى جهاز الكمبيوتر الخاص بك."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1059,6 +1110,10 @@ msgstr "بيان الاعتماد"
 msgid "Credentials"
 msgstr "بيانات الاعتماد"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "تعبير كرون"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "TOTP مفعل للحساب الحالي."
@@ -1092,17 +1147,42 @@ msgstr "الإصدار الحالي"
 msgid "Custom"
 msgstr "مخصص"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "تعبير كرون مخصص"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "دليل مخصص"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "قم بتخصيص اسم العقدة المحلية ليتم عرضها في مؤشر البيئة."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "يومي"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "يوميًا في الساعة %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "لوحة المعلومات"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "يوم الشهر"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "يوم الأسبوع"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "أيام"
@@ -1300,6 +1380,7 @@ msgstr "فشل تعطيل الدفق %{name} من %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "تم تعطيل الدفق %{name} من %{node} بنجاح"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1434,6 +1515,10 @@ msgstr "تم النسخ إلى المحلي بنجاح"
 msgid "Dynamic"
 msgstr "ديناميكي"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "على سبيل المثال، 0 0 * * * (يوميًا عند منتصف الليل)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "تعديل"
@@ -1560,6 +1645,8 @@ msgstr "تفعيل TLS"
 msgid "Enable TOTP"
 msgstr "تفعيل TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1626,6 +1713,18 @@ msgstr "خطأ في معالجة المحتوى"
 msgid "Executable Path"
 msgstr "مسار الملف التنفيذي"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "تنفيذ كل يوم %{day} في الساعة %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "تنفيذ كل يوم في الساعة %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "تنفيذ في كل شهر في اليوم %{day} عند الساعة %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1652,6 +1751,11 @@ msgstr "إشعار خارجي"
 msgid "Fail to obtain certificate"
 msgstr "فشل في الحصول على الشهادة"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "فشل"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "فشل في الاتصال بنموذج التنفيذ: {0}"
@@ -1704,6 +1808,10 @@ msgstr "فشل في نسخ دليل تكوين Nginx: {0}"
 msgid "Failed to create backup"
 msgstr "فشل إنشاء نسخة احتياطية"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "فشل في إنشاء دليل النسخ الاحتياطي: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "فشل إنشاء ملف النسخ الاحتياطي: {0}"
@@ -1728,6 +1836,10 @@ msgstr "فشل إنشاء الدليل الرئيسي: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "فشل في إنشاء دليل الاستعادة: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "فشل في إنشاء دليل التخزين {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "فشل إنشاء رابط رمزي: {0}"
@@ -1940,6 +2052,10 @@ msgstr "فشل بدء الحاوية المؤقتة: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "فشل في التحقق من التجزئات: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "فشل في كتابة ملف النسخ الاحتياطي: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "فشل في كتابة الملف المفكوك تشفيره: {0}"
@@ -1948,6 +2064,10 @@ msgstr "فشل في كتابة الملف المفكوك تشفيره: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "فشل في كتابة الملف المشفر: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "فشل في كتابة ملف مفتاح الأمان: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "فشل الكتابة إلى مخزن zip المؤقت: {0}"
@@ -2003,6 +2123,14 @@ msgstr "تنسيق الكود"
 msgid "Format successfully"
 msgstr "تم التنسيق بنجاح"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "التنسيق: دقيقة ساعة يوم شهر يوم_الأسبوع"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "الجمعة"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "شهادة عامة"
@@ -2079,7 +2207,7 @@ msgstr "القيمة الأعلى تعني إعادة استخدام أفضل ل
 msgid "History"
 msgstr "السجل"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "الصفحة الرئيسية"
 
@@ -2087,6 +2215,10 @@ msgstr "الصفحة الرئيسية"
 msgid "Host"
 msgstr "المضيف"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "ساعة"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2266,6 +2398,10 @@ msgstr "حشو غير صالح في البيانات المشفرة"
 msgid "Invalid passcode or recovery code"
 msgstr "رمز المرور أو رمز الاسترداد غير صالح"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "مسار غير صالح: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "رمز الاسترداد غير صالح"
@@ -2335,6 +2471,14 @@ msgstr "لارك"
 msgid "Lark Custom"
 msgstr "لارك المخصص"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "حالة النسخ الاحتياطي الأخيرة"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "وقت آخر نسخة احتياطية"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "آخر فحص في"
@@ -2428,10 +2572,17 @@ msgstr "جارٍ تحميل البيانات..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "محلي"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "المسار المحلي (مثل، /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "مكان"
@@ -2629,6 +2780,10 @@ msgstr "الحد الأدنى للمساحة الحرة"
 msgid "Minimum free space in the cache directory"
 msgstr "المساحة الحرة الدنيا في دليل التخزين المؤقت"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "دقيقة"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "دقائق"
@@ -2662,11 +2817,24 @@ msgstr "الوحدة"
 msgid "Modules"
 msgstr "الوحدات"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "الاثنين"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "شهري"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "يوميًا في اليوم %{day} الساعة %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "توجيه متعدد الأسطر"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2766,6 +2934,11 @@ msgstr "تكوين Nginx لا يتضمن sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "تكوين Nginx لا يتضمن stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "تكوين Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "لم يتم تعيين دليل تكوين Nginx"
@@ -2903,6 +3076,11 @@ msgstr "أقصى أداء نظري لـ Nginx"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI مثبت بالفعل"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "تكوين واجهة Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "تمت استعادة تكوين Nginx UI"
@@ -3078,6 +3256,7 @@ msgstr "غير متصل"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3086,7 +3265,6 @@ msgstr "غير متصل"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "حسنًا"
 
@@ -3219,6 +3397,10 @@ msgstr "كلمات المرور غير متطابقة"
 msgid "Path"
 msgstr "مسار"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "المسار غير موجود في مسارات الوصول الممنوحة: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "المسار: {0} ليس ضمن دليل nginx conf: {1}"
@@ -3227,6 +3409,11 @@ msgstr "المسار: {0} ليس ضمن دليل nginx conf: {1}"
 msgid "Payload resource is nil"
 msgstr "مورد الحمولة فارغ"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "قيد الانتظار"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "نفذ"
@@ -3294,6 +3481,10 @@ msgstr "الرجاء إدخال رمز الأمان الذي تم استلامه
 msgid "Please fill all fields correctly"
 msgstr "يرجى ملء جميع الحقول بشكل صحيح"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "يرجى ملء حقول تكوين S3 المطلوبة"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3348,8 +3539,8 @@ msgstr "يرجى إدخال كلمة المرور الخاصة بك!"
 msgid "Please input your username!"
 msgstr "يرجى إدخال اسم المستخدم الخاص بك!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "الرجاء تسجيل الدخول."
 
@@ -3361,7 +3552,7 @@ msgstr "يرجى ملاحظة أن تكوين وحدات الوقت أدناه 
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "يرجى حل جميع المشكلات قبل المتابعة مع التثبيت"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "يرجى حفظ رمز الأمان هذا، ستحتاج إليه للاستعادة:"
 
@@ -3828,6 +4019,109 @@ msgstr "وضع التشغيل"
 msgid "Running"
 msgstr "يعمل"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "معرف مفتاح الوصول لـ S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "معرف مفتاح الوصول لـ S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "معرف مفتاح الوصول إلى S3 مطلوب"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "دلو S3"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "تم رفض الوصول إلى حاوية S3: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "دلو S3 مطلوب"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "اسم دلو S3"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "تكوين S3 غير مكتمل: مفقود {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "فشل اختبار اتصال S3"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "فشل اختبار اتصال S3: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "تم اختبار اتصال S3 بنجاح"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "بيانات اعتماد S3 غير صالحة: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "نقطة نهاية S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "نقطة نهاية S3 غير صالحة: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "نقطة نهاية S3 مطلوبة"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "عنوان URL لنقطة نهاية S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "مسار S3 (مثل، backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "منطقة S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "منطقة S3 (مثل us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "مفتاح الوصول السري لـ S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "مفتاح الوصول السري لـ S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "مفتاح الوصول السري لـ S3 مطلوب"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "فشل تحميل S3: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "السبت"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3905,6 +4199,14 @@ msgstr "مسار sbin غير موجود"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "امسح رمز الاستجابة السريعة بهاتفك المحمول لإضافة الحساب إلى التطبيق."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "الجدول الزمني"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "نوع الجدول"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "حزمة تطوير البرمجيات SDK"
@@ -3930,7 +4232,7 @@ msgstr "إعدادات الأمان"
 msgid "Security Token"
 msgstr "رمز الأمان"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "معلومات رمز الأمان"
 
@@ -4187,6 +4489,29 @@ msgstr "متوقف"
 msgid "Storage"
 msgstr "تخزين"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "تكوين التخزين"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "مسار التخزين"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "مسار التخزين مطلوب"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "مسار التخزين غير موجود في مسارات الوصول الممنوحة: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "نوع التخزين"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "تم تمكين البث"
@@ -4211,10 +4536,17 @@ msgstr "دليل Streams-enabled غير موجود"
 msgid "Stub Status Port"
 msgstr "منفذ حالة ستاب"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "نجاح"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "الأحد"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4328,7 +4660,7 @@ msgstr "مزامنة"
 msgid "System"
 msgstr "نظام"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "نسخ احتياطي للنظام"
 
@@ -4345,7 +4677,6 @@ msgid "System Restore"
 msgstr "استعادة النظام"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "تم استعادة النظام بنجاح."
 
@@ -4369,6 +4700,10 @@ msgstr "طرفية"
 msgid "Terminal Start Command"
 msgstr "أمر البدء في المحطة الطرفية"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "اختبار اتصال S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4546,7 +4881,7 @@ msgstr ""
 "هذه العملية ستزيل الشهادة من قاعدة البيانات فقط. لن يتم حذف ملفات الشهادة "
 "على نظام الملفات."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4584,6 +4919,10 @@ msgstr "سيتم ترقية أو إعادة تثبيت Nginx UI على %{nodeNam
 msgid "Throttle"
 msgstr "كبح"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "الخميس"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4700,6 +5039,10 @@ msgstr ""
 msgid "Trash"
 msgstr "مهملات"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "الثلاثاء"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "يتطلب المصادقة الثنائية"
@@ -4716,6 +5059,10 @@ msgstr "نوع"
 msgid "Unknown"
 msgstr "غير معروف"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "نوع النسخ الاحتياطي غير مدعوم: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "تحديث كلمة المرور"
@@ -4728,6 +5075,7 @@ msgstr "تحديث الملف الشخصي"
 msgid "Update successfully"
 msgstr "تم التحديث بنجاح"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4740,7 +5088,7 @@ msgstr "تم التحديث بنجاح"
 msgid "Updated at"
 msgstr "محدث في"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4870,10 +5218,10 @@ msgstr "تمت المشاهدة"
 msgid "Waiting processes"
 msgstr "عمليات الانتظار"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "تحذير"
 
@@ -4913,6 +5261,18 @@ msgstr "إعدادات WebAuthn غير مضبوطة"
 msgid "WebSocket connection error"
 msgstr "خطأ في اتصال WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "الأربعاء"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "أسبوعي"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "أسبوعيًا يوم %{day} الساعة %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4957,7 +5317,7 @@ msgstr "عمليات العامل"
 msgid "Workers"
 msgstr "العمال"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "مساحة العمل"
@@ -5041,6 +5401,9 @@ msgstr "رموزك القديمة لن تعمل بعد الآن."
 msgid "Your passkeys"
 msgstr "مفاتيح المرور الخاصة بك"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "خطأ آخر نسخة احتياطية"
+
 #~ msgid "Apply"
 #~ msgstr "تطبيق"
 
@@ -5074,9 +5437,6 @@ msgstr "مفاتيح المرور الخاصة بك"
 #~ msgid "Ok"
 #~ msgstr "حسنًا"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "يرجى ملء الحقول المطلوبة"
-
 #~ msgid "Recover"
 #~ msgstr "استعادة"
 
@@ -5138,10 +5498,6 @@ msgstr "مفاتيح المرور الخاصة بك"
 #~ msgid "Nginx Conf Include Conf.d"
 #~ msgstr "أمر إعادة تشغيل Nginx"
 
-#, fuzzy
-#~ msgid "Sites Directory"
-#~ msgstr "مجلد"
-
 #~ msgid "Format error %{msg}"
 #~ msgstr "خطأ في التنسيق %{msg}"
 

+ 385 - 23
app/src/language/de_DE/app.po

@@ -107,7 +107,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "2FA-Einstellungen"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Über"
 
@@ -133,6 +133,7 @@ msgstr "ACME-Benutzer"
 msgid "Action"
 msgstr "Aktion"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -351,6 +352,11 @@ msgstr "Auto"
 msgid "auto = CPU cores"
 msgstr "Auto = CPU -Kerne"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Automatische Sicherung"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Automatische Aktualisierung"
@@ -392,7 +398,7 @@ msgstr "Zurück zur Startseite"
 msgid "Back to List"
 msgstr "Zurück zur Liste"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Sicherung"
 
@@ -406,10 +412,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr "Sicherungsdatei nicht gefunden: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "Die Sicherung wurde erfolgreich heruntergeladen"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Sicherungspfad"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "Sicherungspfad existiert nicht: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "Der Sicherungspfad ist kein Verzeichnis: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+"Der Sicherungspfad ist für die benutzerdefinierte Verzeichnissicherung "
+"erforderlich"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Sicherungspfad nicht in den gewährten Zugriffspfaden: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Sicherungszeitplan"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Sicherungstyp"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Schwellenwert für Minuten sperren"
@@ -465,6 +501,14 @@ msgstr "Hier sind die ausgewählten Elemente, die Sie stapelweise ändern möcht
 msgid "Block is nil"
 msgstr "Block ist nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Beide Konfigurationen"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Sowohl Nginx- als auch Nginx-UI-Konfiguration"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Build mit"
@@ -538,6 +582,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Abbrechen"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Auf den Sicherungspfad {0} kann nicht zugegriffen werden: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Auf den Speicherpfad {0} kann nicht zugegriffen werden: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Das Passwort des ersten Benutzers kann im Demo-Modus nicht geändert werden"
@@ -982,12 +1034,12 @@ msgstr "Inhalt"
 msgid "Copied"
 msgstr "Kopiert"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Kopiert!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Kopieren"
 
@@ -1025,7 +1077,7 @@ msgstr "Erstellen"
 msgid "Create Another"
 msgstr "Weiteres erstellen"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Backup erstellen"
 
@@ -1037,7 +1089,7 @@ msgstr "Datei erstellen"
 msgid "Create Folder"
 msgstr "Ordner erstellen"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1046,6 +1098,7 @@ msgstr ""
 "der Nginx-UI-Einstellungen. Die Backup-Dateien werden automatisch auf Ihren "
 "Computer heruntergeladen."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1070,6 +1123,10 @@ msgstr "Zugangsdaten"
 msgid "Credentials"
 msgstr "Zugangsdaten"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron-Ausdruck"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "Aktuelles Konto ist TOTP aktiviert."
@@ -1103,17 +1160,42 @@ msgstr "Aktuelle Version"
 msgid "Custom"
 msgstr "Benutzerdefiniert"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Benutzerdefinierter Cron-Ausdruck"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Benutzerdefiniertes Verzeichnis"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "Name des lokalen Knotens anpassen, der im Umgebungsindikator angezeigt wird."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Täglich"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Täglich um %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Übersicht"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "Tag des Monats"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "Wochentag"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Tage"
@@ -1313,6 +1395,7 @@ msgstr "Deaktivieren des Streams %{name} von %{node} fehlgeschlagen"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Stream %{name} von %{node} erfolgreich deaktiviert"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1452,6 +1535,10 @@ msgstr "Erfolgreich lokal dupliziert"
 msgid "Dynamic"
 msgstr "Dynamisch"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "z. B. 0 0 * * * (täglich um Mitternacht)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Bearbeiten"
@@ -1578,6 +1665,8 @@ msgstr "Aktiviere TLS"
 msgid "Enable TOTP"
 msgstr "TOTP aktivieren"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1644,6 +1733,18 @@ msgstr "Fehler bei der Verarbeitung des Inhalts"
 msgid "Executable Path"
 msgstr "Ausführbarer Pfad"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Ausführen an jedem %{day} um %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Täglich um %{time} ausführen"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Ausführen an jedem Tag %{day} des Monats um %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1670,6 +1771,11 @@ msgstr "Externe Benachrichtigung"
 msgid "Fail to obtain certificate"
 msgstr "Zertifikat konnte nicht abgerufen werden"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Fehlgeschlagen"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Anfügen an die Ausführungsinstanz fehlgeschlagen: {0}"
@@ -1722,6 +1828,10 @@ msgstr "Fehler beim Kopieren des Nginx-Konfigurationsverzeichnisses: {0}"
 msgid "Failed to create backup"
 msgstr "Sicherungskopie konnte nicht erstellt werden"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Fehler beim Erstellen des Backup-Verzeichnisses: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Fehler beim Erstellen der Sicherungsdatei: {0}"
@@ -1746,6 +1856,10 @@ msgstr "Fehler beim Erstellen des übergeordneten Verzeichnisses: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Fehler beim Erstellen des Wiederherstellungsverzeichnisses: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Fehler beim Erstellen des Speicherverzeichnisses {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Symbolische Verknüpfung konnte nicht erstellt werden: {0}"
@@ -1958,6 +2072,10 @@ msgstr "Fehler beim Starten des temporären Containers: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Hash-Überprüfung fehlgeschlagen: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Fehler beim Schreiben der Sicherungsdatei: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Fehler beim Schreiben der entschlüsselten Datei: {0}"
@@ -1966,6 +2084,10 @@ msgstr "Fehler beim Schreiben der entschlüsselten Datei: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Fehler beim Schreiben der verschlüsselten Datei: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Fehler beim Schreiben der Sicherheitsschlüsseldatei: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Fehler beim Schreiben in den Zip-Puffer: {0}"
@@ -2023,6 +2145,14 @@ msgstr "Formatcode"
 msgid "Format successfully"
 msgstr "Erfolgreich formatiert"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Format: Minute Stunde Tag Monat Wochentag"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Freitag"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Allgemeines Zertifikat"
@@ -2099,7 +2229,7 @@ msgstr "Ein höherer Wert bedeutet eine bessere Wiederverwendung der Verbindung"
 msgid "History"
 msgstr "Verlauf"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Startseite"
 
@@ -2107,6 +2237,10 @@ msgstr "Startseite"
 msgid "Host"
 msgstr "Host"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Stunde"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2291,6 +2425,10 @@ msgstr "Ungültige Auffüllung in entschlüsselten Daten"
 msgid "Invalid passcode or recovery code"
 msgstr "Ungültiger Passcode oder Wiederherstellungscode"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Ungültiger Pfad: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Ungültiger Wiederherstellungscode"
@@ -2360,6 +2498,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Benutzerdefiniert"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Letzter Backup-Status"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Letzter Sicherungszeitpunkt"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Zuletzt überprüft am"
@@ -2453,10 +2599,17 @@ msgstr "Daten werden geladen..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Lokal"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Lokaler Pfad (z. B. /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Ort"
@@ -2655,6 +2808,10 @@ msgstr "Mindestfreier Speicherplatz"
 msgid "Minimum free space in the cache directory"
 msgstr "Mindestfreier Speicherplatz im Cache-Verzeichnis"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Minute"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Minuten"
@@ -2688,11 +2845,24 @@ msgstr "Modul"
 msgid "Modules"
 msgstr "Module"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Montag"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Monatlich"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Monatlich am %{day}. um %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Mehrzeilige Direktive"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2792,6 +2962,11 @@ msgstr "Nginx-Konfiguration enthält keine sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx-Konfiguration enthält keinen stream-enabled-Ordner"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Nginx-Konfiguration"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Das Nginx-Konfigurationsverzeichnis ist nicht festgelegt"
@@ -2929,6 +3104,11 @@ msgstr "Theoretische maximale Leistung von Nginx"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI ist bereits installiert"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Nginx-UI-Konfiguration"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Die Nginx-UI-Konfiguration wurde wiederhergestellt"
@@ -3109,6 +3289,7 @@ msgstr "Offline"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3117,7 +3298,6 @@ msgstr "Offline"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "OK"
 
@@ -3250,6 +3430,10 @@ msgstr "Passwörter stimmen nicht überein"
 msgid "Path"
 msgstr "Pfad"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Pfad nicht in den gewährten Zugriffspfaden: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Pfad: {0} befindet sich nicht unter dem nginx-Konfigurationsverzeichnis: {1}"
@@ -3258,6 +3442,11 @@ msgstr "Pfad: {0} befindet sich nicht unter dem nginx-Konfigurationsverzeichnis:
 msgid "Payload resource is nil"
 msgstr "Die Nutzlast-Ressource ist null"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "Ausstehend"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Ausführen"
@@ -3327,6 +3516,10 @@ msgstr "Bitte geben Sie das während der Sicherung erhaltene Sicherheitstoken ei
 msgid "Please fill all fields correctly"
 msgstr "Bitte füllen Sie alle Felder korrekt aus"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Bitte füllen Sie die erforderlichen S3-Konfigurationsfelder aus"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3390,8 +3583,8 @@ msgstr "Bitte gib dein Passwort ein!"
 msgid "Please input your username!"
 msgstr "Bitte gib deinen Benutzernamen ein!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Bitte melden Sie sich an."
 
@@ -3405,7 +3598,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Bitte beheben Sie alle Probleme, bevor Sie mit der Installation fortfahren"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Bitte speichern Sie dieses Sicherheitstoken, Sie benötigen es für die "
@@ -3882,6 +4075,109 @@ msgstr "Betriebsmodus"
 msgid "Running"
 msgstr "Arbeite"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "S3-Zugriffsschlüssel-ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "S3-Zugriffsschlüssel-ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "S3-Zugriffsschlüssel-ID ist erforderlich"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3-Bucket"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Zugriff auf S3-Bucket verweigert: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "S3-Bucket ist erforderlich"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "S3-Bucket-Name"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "S3-Konfiguration ist unvollständig: Fehlend {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "S3-Verbindungstest fehlgeschlagen"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "S3-Verbindungstest fehlgeschlagen: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "S3-Verbindungstest erfolgreich"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "S3-Anmeldeinformationen sind ungültig: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "S3-Endpunkt"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "S3-Endpunkt ist ungültig: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "S3-Endpunkt ist erforderlich"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "S3-Endpunkt-URL"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "S3-Pfad (z. B. backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "S3-Region"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "S3-Region (z. B. us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "S3 geheimer Zugriffsschlüssel"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "S3 Secret Access Key"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "Der geheime S3-Zugangsschlüssel ist erforderlich"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "S3-Upload fehlgeschlagen: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Samstag"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3959,6 +4255,14 @@ msgstr "Sbin-Pfad existiert nicht"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "Scanne den QR-Code mit deinem Handy, um das Konto zur App hinzuzufügen."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Zeitplan"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Zeitplantyp"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3984,7 +4288,7 @@ msgstr "Sicherheitseinstellungen"
 msgid "Security Token"
 msgstr "Sicherheitstoken"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Sicherheitstoken-Informationen"
 
@@ -4247,6 +4551,29 @@ msgstr "Gestoppt"
 msgid "Storage"
 msgstr "Speicher"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Speicherkonfiguration"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Speicherpfad"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "Speicherpfad ist erforderlich"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Speicherpfad nicht in den gewährten Zugriffspfaden: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Speichertyp"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Stream ist aktiviert"
@@ -4271,10 +4598,17 @@ msgstr "Streams-enabled-Verzeichnis existiert nicht"
 msgid "Stub Status Port"
 msgstr "Stub-Status-Port"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Erfolg"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Sonntag"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4393,7 +4727,7 @@ msgstr "Synchronisation"
 msgid "System"
 msgstr "System"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "System-Backup"
 
@@ -4410,7 +4744,6 @@ msgid "System Restore"
 msgstr "Systemwiederherstellung"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "System erfolgreich wiederhergestellt."
 
@@ -4434,6 +4767,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgstr "Terminal-Startbefehl"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "S3-Verbindung testen"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4613,7 +4950,7 @@ msgstr ""
 "Diese Aktion entfernt das Zertifikat nur aus der Datenbank. Die "
 "Zertifikatsdateien im Dateisystem werden nicht gelöscht."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4654,6 +4991,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Begrenzung"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Donnerstag"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4773,6 +5114,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Mülleimer"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Dienstag"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Zwei-Faktor-Authentifizierung erforderlich"
@@ -4789,6 +5134,10 @@ msgstr "Typ"
 msgid "Unknown"
 msgstr "Unbekannt"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Nicht unterstützter Sicherungstyp: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Passwort aktualisieren"
@@ -4801,6 +5150,7 @@ msgstr "Profil aktualisieren"
 msgid "Update successfully"
 msgstr "Erfolgreich aktualisiert"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4813,7 +5163,7 @@ msgstr "Erfolgreich aktualisiert"
 msgid "Updated at"
 msgstr "Aktualisiert am"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4943,10 +5293,10 @@ msgstr "Angesehen"
 msgid "Waiting processes"
 msgstr "Warteverfahren"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Warnung"
 
@@ -4990,6 +5340,18 @@ msgstr "WebAuthn-Einstellungen sind nicht konfiguriert"
 msgid "WebSocket connection error"
 msgstr "WebSocket-Verbindungsfehler"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "Mittwoch"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Wöchentlich"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Wöchentlich am %{day} um %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5040,7 +5402,7 @@ msgstr "Worker-Prozesse"
 msgid "Workers"
 msgstr "Worker"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Arbeitsplatz"
@@ -5128,6 +5490,9 @@ msgstr "Ihre alten Codes funktionieren nicht mehr."
 msgid "Your passkeys"
 msgstr "Deine Passkeys"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Letzter Sicherungsfehler"
+
 #~ msgid "Apply"
 #~ msgstr "Anwenden"
 
@@ -5161,9 +5526,6 @@ msgstr "Deine Passkeys"
 #~ msgid "Ok"
 #~ msgstr "OK"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Bitte fülle die erforderlichen Felder aus"
-
 #~ msgid "Recover"
 #~ msgstr "Wiederherstellen"
 

+ 380 - 20
app/src/language/en/app.po

@@ -91,7 +91,7 @@ msgstr ""
 msgid "2FA Settings"
 msgstr ""
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr ""
 
@@ -117,6 +117,7 @@ msgstr ""
 msgid "Action"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -329,6 +330,11 @@ msgstr ""
 msgid "auto = CPU cores"
 msgstr ""
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr ""
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr ""
@@ -370,7 +376,7 @@ msgstr ""
 msgid "Back to List"
 msgstr ""
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr ""
 
@@ -382,10 +388,38 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr ""
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr ""
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr ""
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr ""
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr ""
@@ -441,6 +475,14 @@ msgstr ""
 msgid "Block is nil"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr ""
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr ""
@@ -510,6 +552,14 @@ msgstr ""
 msgid "Cancel"
 msgstr ""
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr ""
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr ""
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr ""
@@ -917,12 +967,12 @@ msgstr ""
 msgid "Copied"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr ""
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr ""
 
@@ -958,7 +1008,7 @@ msgstr ""
 msgid "Create Another"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr ""
 
@@ -970,12 +1020,13 @@ msgstr ""
 msgid "Create Folder"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1000,6 +1051,10 @@ msgstr ""
 msgid "Credentials"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr ""
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr ""
@@ -1033,17 +1088,42 @@ msgstr ""
 msgid "Custom"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr ""
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr ""
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr ""
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr ""
@@ -1241,6 +1321,7 @@ msgstr ""
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1374,6 +1455,10 @@ msgstr ""
 msgid "Dynamic"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr ""
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr ""
@@ -1500,6 +1585,8 @@ msgstr ""
 msgid "Enable TOTP"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1566,6 +1653,18 @@ msgstr ""
 msgid "Executable Path"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr ""
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1592,6 +1691,11 @@ msgstr ""
 msgid "Fail to obtain certificate"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr ""
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr ""
@@ -1644,6 +1748,10 @@ msgstr ""
 msgid "Failed to create backup"
 msgstr ""
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr ""
@@ -1668,6 +1776,10 @@ msgstr ""
 msgid "Failed to create restore directory: {0}"
 msgstr ""
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr ""
@@ -1880,6 +1992,10 @@ msgstr ""
 msgid "Failed to verify hashes: {0}"
 msgstr ""
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr ""
@@ -1888,6 +2004,10 @@ msgstr ""
 msgid "Failed to write encrypted file: {0}"
 msgstr ""
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr ""
@@ -1943,6 +2063,14 @@ msgstr ""
 msgid "Format successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr ""
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr ""
@@ -2019,7 +2147,7 @@ msgstr ""
 msgid "History"
 msgstr ""
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr ""
 
@@ -2027,6 +2155,10 @@ msgstr ""
 msgid "Host"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr ""
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr ""
@@ -2198,6 +2330,10 @@ msgstr ""
 msgid "Invalid passcode or recovery code"
 msgstr ""
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr ""
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr ""
@@ -2265,6 +2401,14 @@ msgstr ""
 msgid "Lark Custom"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr ""
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr ""
@@ -2358,10 +2502,17 @@ msgstr ""
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr ""
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr ""
@@ -2548,6 +2699,10 @@ msgstr ""
 msgid "Minimum free space in the cache directory"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr ""
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr ""
@@ -2581,11 +2736,24 @@ msgstr ""
 msgid "Modules"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr ""
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr ""
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2685,6 +2853,11 @@ msgstr ""
 msgid "Nginx conf not include stream-enabled"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr ""
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr ""
@@ -2822,6 +2995,11 @@ msgstr ""
 msgid "Nginx UI already installed"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr ""
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr ""
@@ -2993,6 +3171,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3001,7 +3180,6 @@ msgstr ""
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr ""
 
@@ -3131,6 +3309,10 @@ msgstr ""
 msgid "Path"
 msgstr ""
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr ""
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr ""
@@ -3139,6 +3321,11 @@ msgstr ""
 msgid "Payload resource is nil"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr ""
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr ""
@@ -3204,6 +3391,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr ""
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3256,8 +3447,8 @@ msgstr ""
 msgid "Please input your username!"
 msgstr ""
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr ""
 
@@ -3270,7 +3461,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 
@@ -3731,6 +3922,109 @@ msgstr ""
 msgid "Running"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr ""
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr ""
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr ""
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr ""
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr ""
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr ""
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3808,6 +4102,14 @@ msgstr ""
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr ""
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr ""
@@ -3833,7 +4135,7 @@ msgstr ""
 msgid "Security Token"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr ""
 
@@ -4084,6 +4386,29 @@ msgstr ""
 msgid "Storage"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr ""
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr ""
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr ""
@@ -4108,10 +4433,17 @@ msgstr ""
 msgid "Stub Status Port"
 msgstr ""
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr ""
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4219,7 +4551,7 @@ msgstr ""
 msgid "System"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr ""
 
@@ -4236,7 +4568,6 @@ msgid "System Restore"
 msgstr ""
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr ""
 
@@ -4260,6 +4591,10 @@ msgstr ""
 msgid "Terminal Start Command"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr ""
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4413,7 +4748,7 @@ msgid ""
 "certificate files on the file system will not be deleted."
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4446,6 +4781,10 @@ msgstr ""
 msgid "Throttle"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr ""
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4548,6 +4887,10 @@ msgstr ""
 msgid "Trash"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr ""
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr ""
@@ -4564,6 +4907,10 @@ msgstr ""
 msgid "Unknown"
 msgstr ""
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr ""
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr ""
@@ -4576,6 +4923,7 @@ msgstr ""
 msgid "Update successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4588,7 +4936,7 @@ msgstr ""
 msgid "Updated at"
 msgstr ""
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4718,10 +5066,10 @@ msgstr ""
 msgid "Waiting processes"
 msgstr ""
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr ""
 
@@ -4757,6 +5105,18 @@ msgstr ""
 msgid "WebSocket connection error"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr ""
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4796,7 +5156,7 @@ msgstr ""
 msgid "Workers"
 msgstr ""
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr ""

+ 385 - 23
app/src/language/es/app.po

@@ -114,7 +114,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "Configuración de 2FA"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Acerca de"
 
@@ -140,6 +140,7 @@ msgstr "Usuario ACME"
 msgid "Action"
 msgstr "Acción"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -358,6 +359,11 @@ msgstr "Automático"
 msgid "auto = CPU cores"
 msgstr "auto = núcleos de CPU"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Copia de seguridad automática"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Actualización automática"
@@ -399,7 +405,7 @@ msgstr "Volver al Inicio"
 msgid "Back to List"
 msgstr "Volver a la lista"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Copia de seguridad"
 
@@ -413,10 +419,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr "Archivo de respaldo no encontrado: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "La copia de seguridad se ha descargado correctamente"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Ruta de copia de seguridad"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "La ruta de copia de seguridad no existe: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "La ruta de copia de seguridad no es un directorio: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+"La ruta de respaldo es necesaria para el respaldo de directorio "
+"personalizado"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "La ruta de copia de seguridad no está en las rutas de acceso concedidas: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Programa de copia de seguridad"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Tipo de copia de seguridad"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Umbral de Prohibición en Minutos"
@@ -474,6 +510,14 @@ msgstr ""
 msgid "Block is nil"
 msgstr "El bloque es nulo"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Ambas configuraciones"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Tanto la configuración de Nginx como la de la interfaz de usuario de Nginx"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Desarrollado con"
@@ -547,6 +591,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Cancelar"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "No se puede acceder a la ruta de copia de seguridad {0}: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "No se puede acceder a la ruta de almacenamiento {0}: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "No se puede cambiar la contraseña del usuario inicial en modo demo"
@@ -991,12 +1043,12 @@ msgstr "Contenido"
 msgid "Copied"
 msgstr "Copiado"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "¡Copiado!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Copiar"
 
@@ -1034,7 +1086,7 @@ msgstr "Crear"
 msgid "Create Another"
 msgstr "Crear otro"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Crear copia de seguridad"
 
@@ -1046,7 +1098,7 @@ msgstr "Crear Archivo"
 msgid "Create Folder"
 msgstr "Crear carpeta"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1055,6 +1107,7 @@ msgstr ""
 "y los ajustes de Nginx UI. Los archivos de respaldo se descargarán "
 "automáticamente en tu computadora."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1079,6 +1132,10 @@ msgstr "Credencial"
 msgid "Credentials"
 msgstr "Credenciales"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Expresión Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "La cuenta actual tiene habilitada TOTP."
@@ -1112,6 +1169,15 @@ msgstr "Versión actual"
 msgid "Custom"
 msgstr "Personalizado"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Expresión cron personalizada"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Directorio personalizado"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1120,11 +1186,27 @@ msgstr ""
 "Personalice el nombre del servidor local para mostrarlo en el indicador de "
 "entorno."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Diario"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Diariamente a las %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Panel"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "Día del mes"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "Día de la semana"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Días"
@@ -1324,6 +1406,7 @@ msgstr "Desactivar el flujo %{name} desde %{node} falló"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Deshabilitar el flujo %{name} desde %{node} con éxito"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1461,6 +1544,10 @@ msgstr "Duplicado con éxito a local"
 msgid "Dynamic"
 msgstr "Dinámico"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "p. ej., 0 0 * * * (diariamente a medianoche)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Editar"
@@ -1587,6 +1674,8 @@ msgstr "Habilitar TLS"
 msgid "Enable TOTP"
 msgstr "Habilitar TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1653,6 +1742,18 @@ msgstr "Error al procesar el contenido"
 msgid "Executable Path"
 msgstr "Ruta ejecutable"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Ejecutar cada %{day} a las %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Ejecutar todos los días a las %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Ejecutar cada mes el día %{day} a las %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1679,6 +1780,11 @@ msgstr "Notificación Externa"
 msgid "Fail to obtain certificate"
 msgstr "Falla al obtener el certificado"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Fallido"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Error al adjuntar a la instancia de ejecución: {0}"
@@ -1733,6 +1839,10 @@ msgstr "Error al copiar el directorio de configuración de Nginx: {0}"
 msgid "Failed to create backup"
 msgstr "No se pudo crear la copia de seguridad"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Error al crear el directorio de respaldo: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Error al crear el archivo de respaldo: {0}"
@@ -1757,6 +1867,10 @@ msgstr "Error al crear el directorio principal: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Error al crear el directorio de restauración: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Error al crear el directorio de almacenamiento {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Error al crear el enlace simbólico: {0}"
@@ -1969,6 +2083,10 @@ msgstr "Error al iniciar el contenedor temporal: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Error al verificar los hashes: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Error al escribir el archivo de respaldo: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Error al escribir el archivo desencriptado: {0}"
@@ -1977,6 +2095,10 @@ msgstr "Error al escribir el archivo desencriptado: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Error al escribir el archivo cifrado: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Error al escribir el archivo de clave de seguridad: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Error al escribir en el búfer ZIP: {0}"
@@ -2034,6 +2156,14 @@ msgstr "Código de formato"
 msgid "Format successfully"
 msgstr "Formateado correctamente"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Formato: minuto hora día mes día_de_la_semana"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Viernes"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Certificado General"
@@ -2110,7 +2240,7 @@ msgstr "Un valor más alto significa una mejor reutilización de la conexión"
 msgid "History"
 msgstr "Historial"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Inicio"
 
@@ -2118,6 +2248,10 @@ msgstr "Inicio"
 msgid "Host"
 msgstr "Host"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Hora"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2298,6 +2432,10 @@ msgstr "Relleno no válido en los datos descifrados"
 msgid "Invalid passcode or recovery code"
 msgstr "Código de acceso o código de recuperación inválido"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Ruta no válida: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Código de recuperación no válido"
@@ -2367,6 +2505,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Personalizado"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Estado del último respaldo"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Hora del último respaldo"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Comprobado por última vez el"
@@ -2460,10 +2606,17 @@ msgstr "Cargando datos..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Local"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Ruta local (ej., /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Ubicación"
@@ -2663,6 +2816,10 @@ msgstr "Espacio libre mínimo"
 msgid "Minimum free space in the cache directory"
 msgstr "Espacio libre mínimo en el directorio de caché"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Minuto"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Minutos"
@@ -2696,11 +2853,24 @@ msgstr "Módulo"
 msgid "Modules"
 msgstr "Módulos"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Lunes"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Mensual"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Mensualmente el día %{day} a las %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Directiva multilínea"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2800,6 +2970,11 @@ msgstr "La configuración de Nginx no incluye sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "La configuración de Nginx no incluye stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Configuración de Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "El directorio de configuración de Nginx no está establecido"
@@ -2937,6 +3112,11 @@ msgstr "Rendimiento teórico máximo de Nginx"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI ya está instalado"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Configuración de la interfaz de Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "La configuración de Nginx UI ha sido restaurada"
@@ -3117,6 +3297,7 @@ msgstr "Desconectado"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3125,7 +3306,6 @@ msgstr "Desconectado"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "OK"
 
@@ -3259,6 +3439,10 @@ msgstr "Las contraseñas no coinciden"
 msgid "Path"
 msgstr "Ruta"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "La ruta no está en las rutas de acceso concedidas: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "La ruta: {0} no está dentro del directorio de configuración de nginx: {1}"
@@ -3267,6 +3451,11 @@ msgstr "La ruta: {0} no está dentro del directorio de configuración de nginx:
 msgid "Payload resource is nil"
 msgstr "El recurso de carga útil es nulo"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "Pendiente"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Realizar"
@@ -3338,6 +3527,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgstr "Por favor, complete todos los campos correctamente"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Por favor, complete los campos de configuración de S3 requeridos"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3401,8 +3594,8 @@ msgstr "¡Por favor ingrese su contraseña!"
 msgid "Please input your username!"
 msgstr "¡Por favor ingrese su nombre de usuario!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Por favor, inicie sesión."
 
@@ -3416,7 +3609,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Por favor, resuelva todos los problemas antes de proceder con la instalación"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Por favor, guarde este token de seguridad, lo necesitará para la "
@@ -3889,6 +4082,109 @@ msgstr "Modo de ejecución"
 msgid "Running"
 msgstr "Corriendo"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "ID de clave de acceso de S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "ID de clave de acceso de S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "Se requiere el ID de clave de acceso de S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "Cubo S3"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Acceso al depósito S3 denegado: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "Se requiere el bucket S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "Nombre del bucket S3"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "La configuración de S3 está incompleta: falta {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "Prueba de conexión S3 fallida"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "Prueba de conexión S3 fallida: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "Prueba de conexión S3 exitosa"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "Las credenciales de S3 no son válidas: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "Punto de conexión S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "El punto de conexión de S3 no es válido: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "Se requiere el punto de conexión S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "URL del punto de conexión S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "Ruta S3 (ej., backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "Región S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "Región S3 (p. ej., us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "Clave de acceso secreta de S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "Clave de acceso secreta de S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "Se requiere la clave de acceso secreta de S3"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "Error al subir a S3: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Sábado"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3968,6 +4264,14 @@ msgstr ""
 "Escanee el código QR con su teléfono móvil para agregar la cuenta a la "
 "aplicación."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Programación"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Tipo de programación"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3993,7 +4297,7 @@ msgstr "Configuración de seguridad"
 msgid "Security Token"
 msgstr "Token de seguridad"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Información del token de seguridad"
 
@@ -4256,6 +4560,29 @@ msgstr "Detenido"
 msgid "Storage"
 msgstr "Almacenamiento"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Configuración de almacenamiento"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Ruta de almacenamiento"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "La ruta de almacenamiento es obligatoria"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "La ruta de almacenamiento no está en las rutas de acceso concedidas: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Tipo de almacenamiento"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "La transmisión está habilitada"
@@ -4280,10 +4607,17 @@ msgstr "El directorio streams-enabled no existe"
 msgid "Stub Status Port"
 msgstr "Puerto de estado stub"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Éxito"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Domingo"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4400,7 +4734,7 @@ msgstr "Sincronización"
 msgid "System"
 msgstr "Sistema"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Copia de seguridad del sistema"
 
@@ -4417,7 +4751,6 @@ msgid "System Restore"
 msgstr "Restauración del sistema"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Sistema restaurado correctamente."
 
@@ -4441,6 +4774,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgstr "Comando de inicio de terminal"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Probar conexión S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4621,7 +4958,7 @@ msgstr ""
 "Esta operación solo eliminará el certificado de la base de datos. Los "
 "archivos del certificado en el sistema de archivos no se eliminarán."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4661,6 +4998,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Acelerador"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Jueves"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4781,6 +5122,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Basura"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Martes"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Se requiere autenticación de dos factores"
@@ -4797,6 +5142,10 @@ msgstr "Tipo"
 msgid "Unknown"
 msgstr "Desconocido"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Tipo de copia de seguridad no admitido: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Actualizar contraseña"
@@ -4809,6 +5158,7 @@ msgstr "Actualizar perfil"
 msgid "Update successfully"
 msgstr "Actualización exitosa"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4821,7 +5171,7 @@ msgstr "Actualización exitosa"
 msgid "Updated at"
 msgstr "Actualizado a"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4951,10 +5301,10 @@ msgstr "Visto"
 msgid "Waiting processes"
 msgstr "Procesos de espera"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Advertencia"
 
@@ -4997,6 +5347,18 @@ msgstr "La configuración de WebAuthn no está configurada"
 msgid "WebSocket connection error"
 msgstr "Error de conexión WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "miércoles"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Semanal"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Semanalmente el %{day} a las %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5044,7 +5406,7 @@ msgstr "Procesos de trabajo"
 msgid "Workers"
 msgstr "Trabajadores"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Espacio de trabajo"
@@ -5131,6 +5493,9 @@ msgstr "Tus códigos antiguos ya no funcionarán."
 msgid "Your passkeys"
 msgstr "Sus llaves de acceso"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Último error de copia de seguridad"
+
 #~ msgid "Apply"
 #~ msgstr "Aplicar"
 
@@ -5155,9 +5520,6 @@ msgstr "Sus llaves de acceso"
 #~ msgid "Ok"
 #~ msgstr "Ok"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Por favor, complete los campos requeridos"
-
 #~ msgid "Recover"
 #~ msgstr "Recuperar"
 

+ 385 - 27
app/src/language/fr_FR/app.po

@@ -112,7 +112,7 @@ msgstr "2fa"
 msgid "2FA Settings"
 msgstr "Options 2FA"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "À propos"
 
@@ -138,6 +138,7 @@ msgstr "Utilisateur ACME"
 msgid "Action"
 msgstr "Action"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -356,6 +357,11 @@ msgstr "Auto"
 msgid "auto = CPU cores"
 msgstr "auto = cœurs CPU"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Sauvegarde automatique"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Actualisation automatique"
@@ -397,7 +403,7 @@ msgstr "Retour au menu principal"
 msgid "Back to List"
 msgstr "Retour à la liste"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Sauvegarde"
 
@@ -411,10 +417,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr "Fichier de sauvegarde introuvable : {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "La sauvegarde a été téléchargée avec succès"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Chemin de sauvegarde"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "Le chemin de sauvegarde n'existe pas : {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "Le chemin de sauvegarde n'est pas un répertoire : {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+"Le chemin de sauvegarde est requis pour la sauvegarde de répertoire "
+"personnalisée"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Le chemin de sauvegarde n'est pas dans les chemins d'accès accordés : {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Planification de sauvegarde"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Type de sauvegarde"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Minutes seuil de bannissement"
@@ -470,6 +506,14 @@ msgstr "Ci-dessous sont sélectionnés les éléments que vous voulez modifier e
 msgid "Block is nil"
 msgstr "Le bloc est nul"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Les deux configurations"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Configuration à la fois de Nginx et de l'interface utilisateur Nginx"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Build avec"
@@ -542,6 +586,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Annuler"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Impossible d'accéder au chemin de sauvegarde {0} : {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Impossible d'accéder au chemin de stockage {0} : {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Impossible de modifier le mot de passe de l'utilisateur initial en mode démo"
@@ -987,12 +1039,12 @@ msgstr "Contenu"
 msgid "Copied"
 msgstr "Copié"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Copié !"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Copier"
 
@@ -1030,7 +1082,7 @@ msgstr "Créer"
 msgid "Create Another"
 msgstr "Créer un autre"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Créer une sauvegarde"
 
@@ -1042,7 +1094,7 @@ msgstr "Créer un fichier"
 msgid "Create Folder"
 msgstr "Créer un dossier"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1051,6 +1103,7 @@ msgstr ""
 "paramètres de Nginx UI. Les fichiers de sauvegarde seront automatiquement "
 "téléchargés sur votre ordinateur."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1075,6 +1128,10 @@ msgstr "Identifiant"
 msgid "Credentials"
 msgstr "Identifiants"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Expression Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "Le compte actuel a le TOTP d'activé."
@@ -1108,6 +1165,15 @@ msgstr "Version actuelle"
 msgid "Custom"
 msgstr "Custom"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Expression cron personnalisée"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Répertoire personnalisé"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1116,11 +1182,27 @@ msgstr ""
 "Personnalisez le nom du nœud local à afficher dans l'indicateur "
 "d'environnement."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Quotidien"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Quotidiennement à %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Dashboard"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "Jour du mois"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "Jour de la semaine"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Jours"
@@ -1320,6 +1402,7 @@ msgstr "Échec de la désactivation du flux %{name} depuis %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Désactivation du flux %{name} depuis %{node} réussie"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1455,6 +1538,10 @@ msgstr "Duplication locale réussie"
 msgid "Dynamic"
 msgstr "Dynamique"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "par ex., 0 0 * * * (quotidiennement à minuit)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Modifier"
@@ -1581,6 +1668,8 @@ msgstr "Activer TLS"
 msgid "Enable TOTP"
 msgstr "Activer TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1647,6 +1736,18 @@ msgstr "Erreur lors du traitement du contenu"
 msgid "Executable Path"
 msgstr "Chemin exécutable"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Exécuter chaque %{day} à %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Exécuter tous les jours à %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Exécuter chaque mois le jour %{day} à %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1673,6 +1774,11 @@ msgstr "Notification Externe"
 msgid "Fail to obtain certificate"
 msgstr "Échec de l'obtention du certificat"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Échec"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Échec de l'attachement à l'instance d'exécution : {0}"
@@ -1725,6 +1831,10 @@ msgstr "Échec de la copie du répertoire de configuration Nginx : {0}"
 msgid "Failed to create backup"
 msgstr "Échec de la création de la sauvegarde"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Échec de la création du répertoire de sauvegarde : {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Échec de la création du fichier de sauvegarde : {0}"
@@ -1749,6 +1859,10 @@ msgstr "Échec de la création du répertoire parent : {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Échec de la création du répertoire de restauration : {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Échec de la création du répertoire de stockage {0} : {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Échec de la création du lien symbolique : {0}"
@@ -1963,6 +2077,10 @@ msgstr "Échec du démarrage du conteneur temporaire : {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Échec de la vérification des hachages : {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Échec de l'écriture du fichier de sauvegarde : {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Échec de l'écriture du fichier décrypté : {0}"
@@ -1971,6 +2089,10 @@ msgstr "Échec de l'écriture du fichier décrypté : {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Échec de l'écriture du fichier chiffré : {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Échec de l'écriture du fichier de clé de sécurité : {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Échec de l'écriture dans le tampon ZIP : {0}"
@@ -2028,6 +2150,14 @@ msgstr "Code de formatage"
 msgid "Format successfully"
 msgstr "Formaté avec succès"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Format : minute heure jour mois jour_de_la_semaine"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Vendredi"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Certificat général"
@@ -2104,7 +2234,7 @@ msgstr "Une valeur plus élevée signifie une meilleure réutilisation de la con
 msgid "History"
 msgstr "Historique"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Menu principal"
 
@@ -2112,6 +2242,10 @@ msgstr "Menu principal"
 msgid "Host"
 msgstr "Hôte"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Heure"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2296,6 +2430,10 @@ msgstr "Remplissage invalide dans les données décryptées"
 msgid "Invalid passcode or recovery code"
 msgstr "Code de vérification ou code de récupération invalide"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Chemin invalide : {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Code de récupération invalide"
@@ -2365,6 +2503,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Personnalisé"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Statut de la dernière sauvegarde"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Dernière heure de sauvegarde"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Dernière vérification le"
@@ -2458,10 +2604,17 @@ msgstr "Chargement des données..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Local"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Chemin local (par ex., /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Emplacement"
@@ -2661,6 +2814,10 @@ msgstr "Espace libre minimum"
 msgid "Minimum free space in the cache directory"
 msgstr "Espace libre minimum dans le répertoire de cache"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Minute"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Minutes"
@@ -2694,11 +2851,24 @@ msgstr "Module"
 msgid "Modules"
 msgstr "Modules"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Lundi"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Mensuel"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Mensuellement le jour %{day} à %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Directive multiligne"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2798,6 +2968,11 @@ msgstr "La configuration Nginx n'inclut pas sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "La configuration Nginx n'inclut pas stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Configuration Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Le répertoire de configuration de Nginx n'est pas défini"
@@ -2935,6 +3110,11 @@ msgstr "Performances théoriques maximales de Nginx"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI est déjà installé"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Configuration de l'interface Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "La configuration de Nginx UI a été restaurée"
@@ -3114,6 +3294,7 @@ msgstr "Hors ligne"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3122,7 +3303,6 @@ msgstr "Hors ligne"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "OK"
 
@@ -3256,6 +3436,10 @@ msgstr "Les mots de passe ne correspondent pas"
 msgid "Path"
 msgstr "Chemin"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Le chemin n'est pas dans les chemins d'accès accordés : {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr ""
@@ -3266,6 +3450,11 @@ msgstr ""
 msgid "Payload resource is nil"
 msgstr "La ressource de charge utile est nulle"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "En attente"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Exécuter"
@@ -3335,6 +3524,10 @@ msgstr "Veuillez saisir le jeton de sécurité reçu lors de la sauvegarde"
 msgid "Please fill all fields correctly"
 msgstr "Veuillez remplir tous les champs correctement"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Veuillez remplir les champs de configuration S3 requis"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3398,8 +3591,8 @@ msgstr "Veuillez saisir votre mot de passe !"
 msgid "Please input your username!"
 msgstr "Veuillez saisir votre nom d'utilisateur !"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Veuillez vous connecter."
 
@@ -3413,7 +3606,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Veuillez résoudre tous les problèmes avant de procéder à l'installation"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Veuillez enregistrer ce jeton de sécurité, vous en aurez besoin pour la "
@@ -3888,6 +4081,109 @@ msgstr "Mode d'exécution"
 msgid "Running"
 msgstr "En cours d'éxécution"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "ID de clé d'accès S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "ID de clé d'accès S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "L'ID de clé d'accès S3 est requis"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "Bucket S3"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Accès au compartiment S3 refusé : {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "Le bucket S3 est requis"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "Nom du bucket S3"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "La configuration S3 est incomplète : il manque {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "Échec du test de connexion S3"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "Échec du test de connexion S3 : {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "Test de connexion S3 réussi"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "Les identifiants S3 sont invalides : {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "Point de terminaison S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "Le point de terminaison S3 est invalide : {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "Le point de terminaison S3 est requis"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "URL du point de terminaison S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "Chemin S3 (par ex., backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "Région S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "Région S3 (par ex., us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "Clé d'accès secrète S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "Clé d'accès secrète S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "La clé d'accès secrète S3 est requise"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "Échec du téléversement S3 : {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Samedi"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3967,6 +4263,14 @@ msgstr ""
 "Scannez le code QR avec votre téléphone portable pour ajouter le compte à "
 "l'application."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Planification"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Type de planification"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3992,7 +4296,7 @@ msgstr "Paramètres de sécurité"
 msgid "Security Token"
 msgstr "Jeton de sécurité"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Informations sur le jeton de sécurité"
 
@@ -4255,6 +4559,29 @@ msgstr "Arrêté"
 msgid "Storage"
 msgstr "Stockage"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Configuration du stockage"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Chemin de stockage"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "Le chemin de stockage est requis"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Le chemin de stockage n'est pas dans les chemins d'accès accordés: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Type de stockage"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Le flux est activé"
@@ -4279,10 +4606,17 @@ msgstr "Le répertoire streams-enabled n'existe pas"
 msgid "Stub Status Port"
 msgstr "Port d'état stub"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Succès"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Dimanche"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4401,7 +4735,7 @@ msgstr "Synchronisation"
 msgid "System"
 msgstr "Système"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Sauvegarde du système"
 
@@ -4418,7 +4752,6 @@ msgid "System Restore"
 msgstr "Restauration du système"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Système restauré avec succès."
 
@@ -4442,6 +4775,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgstr "Commande de démarrage du terminal"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Tester la connexion S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4627,7 +4964,7 @@ msgstr ""
 "Cette opération ne supprimera que le certificat de la base de données. Les "
 "fichiers de certificat sur le système de fichiers ne seront pas supprimés."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4668,6 +5005,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Limitation"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Jeudi"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4789,6 +5130,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Corbeille"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Mardi"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Authentification à deux facteurs requise"
@@ -4805,6 +5150,10 @@ msgstr "Type"
 msgid "Unknown"
 msgstr "Inconnu"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Type de sauvegarde non pris en charge : {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Mettre à jour le mot de passe"
@@ -4817,6 +5166,7 @@ msgstr "Mettre à jour le profil"
 msgid "Update successfully"
 msgstr "Mise à jour réussie"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4829,7 +5179,7 @@ msgstr "Mise à jour réussie"
 msgid "Updated at"
 msgstr "Mis à jour le"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4959,10 +5309,10 @@ msgstr "Vu"
 msgid "Waiting processes"
 msgstr "Processus d'attente"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Avertissement"
 
@@ -5005,6 +5355,18 @@ msgstr "Les paramètres WebAuthn ne sont pas configurés"
 msgid "WebSocket connection error"
 msgstr "Erreur de connexion WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "mercredi"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Hebdomadaire"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Hebdomadairement le %{day} à %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5055,7 +5417,7 @@ msgstr "Processus de travail"
 msgid "Workers"
 msgstr "Workers"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Espace de travail"
@@ -5142,6 +5504,9 @@ msgstr "Vos anciens codes ne fonctionneront plus."
 msgid "Your passkeys"
 msgstr "Vos clés d'accès"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Dernière erreur de sauvegarde"
+
 #~ msgid "Apply"
 #~ msgstr "Appliquer"
 
@@ -5166,9 +5531,6 @@ msgstr "Vos clés d'accès"
 #~ msgid "Ok"
 #~ msgstr "Ok"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Veuillez remplir les champs obligatoires"
-
 #~ msgid "Recover"
 #~ msgstr "Récupérer"
 
@@ -5232,10 +5594,6 @@ msgstr "Vos clés d'accès"
 #~ msgid "Nginx Conf Include Conf.d"
 #~ msgstr "Commande de démarrage du terminal"
 
-#, fuzzy
-#~ msgid "Sites Directory"
-#~ msgstr "Directive"
-
 #~ msgid "Format error %{msg}"
 #~ msgstr "Erreur de format %{msg}"
 

+ 383 - 23
app/src/language/ja_JP/app.po

@@ -108,7 +108,7 @@ msgstr "二要素認証"
 msgid "2FA Settings"
 msgstr "ニ要素認証設定"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Nginx UI について"
 
@@ -134,6 +134,7 @@ msgstr "ACMEユーザー"
 msgid "Action"
 msgstr "操作"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -346,6 +347,11 @@ msgstr "自動"
 msgid "auto = CPU cores"
 msgstr "自動 = CPUコア数"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "自動バックアップ"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "自動更新"
@@ -387,7 +393,7 @@ msgstr "ホームに戻る"
 msgid "Back to List"
 msgstr "リストに戻る"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "バックアップ"
 
@@ -399,10 +405,38 @@ msgstr "「バックアップファイルの整合性チェックに失敗しま
 msgid "Backup file not found: {0}"
 msgstr "バックアップファイルが見つかりません:{0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "バックアップは正常にダウンロードされました"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "バックアップパス"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "バックアップパスが存在しません: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "バックアップパスはディレクトリではありません: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "カスタムディレクトリのバックアップにはバックアップパスが必要です"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "バックアップパスが許可されたアクセスパスにありません: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "バックアップスケジュール"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "バックアップタイプ"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "制限閾値(分)"
@@ -458,6 +492,14 @@ msgstr "以下は一括修正したい選択項目です"
 msgid "Block is nil"
 msgstr "ブロックがnilです"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "両方の設定"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "NginxとNginx UIの両方の設定"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "ビルド環境"
@@ -529,6 +571,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "キャンセル"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "バックアップパス {0} にアクセスできません: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "ストレージパス {0} にアクセスできません: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "デモモードで初期ユーザーパスワードを変更できません"
@@ -956,12 +1006,12 @@ msgstr "コンテンツ"
 msgid "Copied"
 msgstr "コピーしました"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "コピーしました!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "コピー"
 
@@ -997,7 +1047,7 @@ msgstr "作成"
 msgid "Create Another"
 msgstr "別の設定を作成"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "バックアップを作成"
 
@@ -1009,12 +1059,13 @@ msgstr "ファイルを作成"
 msgid "Create Folder"
 msgstr "フォルダーを作成"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 msgstr "Nginx 設定と Nginx UI 設定を含むシステムバックアップを作成します。バックアップファイルは自動的にコンピュータにダウンロードされます。"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1039,6 +1090,10 @@ msgstr "認証情報"
 msgid "Credentials"
 msgstr "認証情報"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron式"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "現在のアカウントは TOTP が有効です。"
@@ -1072,17 +1127,42 @@ msgstr "現在のバージョン"
 msgid "Custom"
 msgstr "カスタム"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "カスタム cron 式"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "カスタムディレクトリ"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "環境インジケーターに表示するローカルノードの名前をカスタマイズします。"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "毎日"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "毎日 %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "ダッシュボード"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "月の日"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "曜日"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "日"
@@ -1280,6 +1360,7 @@ msgstr "ノード %{node} からのストリーム %{name} の無効化に失敗
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "ストリーム %{name} を %{node} から無効化しました"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1412,6 +1493,10 @@ msgstr "ローカルへの複製が成功しました"
 msgid "Dynamic"
 msgstr "動的"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "例: 0 0 * * * (毎日深夜0時)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "編集"
@@ -1538,6 +1623,8 @@ msgstr "TLSを有効にする"
 msgid "Enable TOTP"
 msgstr "TOTP を有効にする"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1604,6 +1691,18 @@ msgstr "コンテンツ処理エラー"
 msgid "Executable Path"
 msgstr "実行可能ファイルのパス"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "毎週%{day}の%{time}に実行"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "毎日 %{time} に実行"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "毎月 %{day} 日の %{time} に実行"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1630,6 +1729,11 @@ msgstr "外部通知"
 msgid "Fail to obtain certificate"
 msgstr "証明書の取得に失敗しました"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "失敗"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "exec インスタンスへのアタッチに失敗しました: {0}"
@@ -1682,6 +1786,10 @@ msgstr "Nginx設定ディレクトリのコピーに失敗しました: {0}"
 msgid "Failed to create backup"
 msgstr "バックアップの作成に失敗しました"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "バックアップディレクトリの作成に失敗しました: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "バックアップファイルの作成に失敗しました: {0}"
@@ -1706,6 +1814,10 @@ msgstr "親ディレクトリの作成に失敗しました: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "復元ディレクトリの作成に失敗しました: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "ストレージディレクトリの作成に失敗しました {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "シンボリックリンクの作成に失敗しました: {0}"
@@ -1918,6 +2030,10 @@ msgstr "一時コンテナの起動に失敗しました: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "ハッシュの検証に失敗しました: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "バックアップファイルの書き込みに失敗しました: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "復号化ファイルの書き込みに失敗しました: {0}"
@@ -1926,6 +2042,10 @@ msgstr "復号化ファイルの書き込みに失敗しました: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "暗号化ファイルの書き込みに失敗しました: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "セキュリティキーファイルの書き込みに失敗しました: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "ZIPバッファへの書き込みに失敗しました: {0}"
@@ -1981,6 +2101,14 @@ msgstr "コードをフォーマット"
 msgid "Format successfully"
 msgstr "フォーマットが成功しました"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "形式: 分 時 日 月 曜日"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "金曜日"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "一般証明書"
@@ -2057,7 +2185,7 @@ msgstr "値が高いほど接続の再利用が効率的であることを意味
 msgid "History"
 msgstr "履歴"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "ホーム"
 
@@ -2065,6 +2193,10 @@ msgstr "ホーム"
 msgid "Host"
 msgstr "ホスト"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "時間"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2236,6 +2368,10 @@ msgstr "復号化データのパディングが無効です"
 msgid "Invalid passcode or recovery code"
 msgstr "無効なパスコードまたはリカバリーコード"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "無効なパス: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "無効なリカバリーコード"
@@ -2303,6 +2439,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark カスタム"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "最終バックアップステータス"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "最終バックアップ時刻"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "最終確認日時"
@@ -2396,10 +2540,17 @@ msgstr "データを読み込んでいます..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "ローカル"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "ローカルパス(例:/var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "ロケーション"
@@ -2594,6 +2745,10 @@ msgstr "最小空き容量"
 msgid "Minimum free space in the cache directory"
 msgstr "キャッシュディレクトリの最小空き容量"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "分"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "分"
@@ -2627,11 +2782,24 @@ msgstr "モジュール"
 msgid "Modules"
 msgstr "モジュール"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "月曜日"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "毎月"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "毎月%{day}日%{time}に"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "複数行ディレクティブ"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2731,6 +2899,11 @@ msgstr "Nginx設定にsites-enabledが含まれていません"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx設定にstream-enabledが含まれていません"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Nginx設定"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Nginx設定ディレクトリが設定されていません"
@@ -2868,6 +3041,11 @@ msgstr "Nginx の理論上の最大パフォーマンス"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI はすでにインストールされています"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Nginx UI 設定"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Nginx UI の設定が復元されました"
@@ -3039,6 +3217,7 @@ msgstr "オフライン"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3047,7 +3226,6 @@ msgstr "オフライン"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "OK"
 
@@ -3179,6 +3357,10 @@ msgstr "パスワードが一致しません"
 msgid "Path"
 msgstr "パス"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "許可されたアクセスパスに含まれないパス: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "パス: {0} は nginx の設定ディレクトリ: {1} の下にありません"
@@ -3187,6 +3369,11 @@ msgstr "パス: {0} は nginx の設定ディレクトリ: {1} の下にあり
 msgid "Payload resource is nil"
 msgstr "ペイロードリソースが nil です"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "保留中"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "実行"
@@ -3252,6 +3439,10 @@ msgstr "バックアップ時に受け取ったセキュリティトークンを
 msgid "Please fill all fields correctly"
 msgstr "すべての項目を正しく入力してください"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "必要なS3設定項目を入力してください"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3306,8 +3497,8 @@ msgstr "パスワードを入力してください!"
 msgid "Please input your username!"
 msgstr "ユーザー名を入力してください!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "ログインしてください。"
 
@@ -3319,7 +3510,7 @@ msgstr "以下の時間設定の単位はすべて秒であることに注意し
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "インストールを続行する前にすべての問題を解決してください"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "このセキュリティトークンを保存してください。復元時に必要になります:"
 
@@ -3780,6 +3971,109 @@ msgstr "実行モード"
 msgid "Running"
 msgstr "実行中"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "S3アクセスキーID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "S3 アクセスキー ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "S3アクセスキーIDが必要です"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3 バケット"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "S3バケットへのアクセスが拒否されました: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "S3バケットが必要です"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "S3バケット名"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "S3設定が不完全です: {0}が不足しています"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "S3接続テストが失敗しました"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "S3接続テストが失敗しました: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "S3接続テストが成功しました"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "S3の認証情報が無効です: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "S3 エンドポイント"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "S3エンドポイントが無効です: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "S3 エンドポイントが必要です"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "S3エンドポイントURL"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "S3 パス(例:backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "S3リージョン"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "S3リージョン(例:us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "S3シークレットアクセスキー"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "S3 シークレットアクセスキー"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "S3シークレットアクセスキーが必要です"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "S3アップロードに失敗しました: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "土曜日"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3857,6 +4151,14 @@ msgstr "sbin パスが存在しません"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "スマートフォンでQRコードをスキャンして、アプリにアカウントを追加します。"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "スケジュール"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "スケジュールタイプ"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3882,7 +4184,7 @@ msgstr "セキュリティ設定"
 msgid "Security Token"
 msgstr "セキュリティトークン"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "セキュリティトークン情報"
 
@@ -4137,6 +4439,29 @@ msgstr "停止"
 msgid "Storage"
 msgstr "ストレージ"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "ストレージ設定"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "保存先パス"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "ストレージパスは必須です"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "ストレージパスが許可されたアクセスパスにありません: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "ストレージタイプ"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "ストリームが有効です"
@@ -4161,10 +4486,17 @@ msgstr "streams-enabled ディレクトリが存在しません"
 msgid "Stub Status Port"
 msgstr "スタブステータスポート"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "成功"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "日曜日"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4278,7 +4610,7 @@ msgstr "同期"
 msgid "System"
 msgstr "システム"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "システムバックアップ"
 
@@ -4295,7 +4627,6 @@ msgid "System Restore"
 msgstr "システム復元"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "システムの復元が完了しました。"
 
@@ -4319,6 +4650,10 @@ msgstr "ターミナル"
 msgid "Terminal Start Command"
 msgstr "ターミナル起動コマンド"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "S3接続をテスト"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4475,7 +4810,7 @@ msgid ""
 "certificate files on the file system will not be deleted."
 msgstr "この操作はデータベースから証明書を削除するのみです。ファイルシステム上の証明書ファイルは削除されません。"
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4507,6 +4842,10 @@ msgstr "これにより、%{nodeNames} 上の Nginx UI が %{version} にアッ
 msgid "Throttle"
 msgstr "スロットル"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "木曜日"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4617,6 +4956,10 @@ msgstr "TOTP は、時間ベースのワンタイムパスワードアルゴリ
 msgid "Trash"
 msgstr "ゴミ箱"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "火曜日"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "二要素認証が必要です"
@@ -4633,6 +4976,10 @@ msgstr "タイプ"
 msgid "Unknown"
 msgstr "不明"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "サポートされていないバックアップタイプ: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "パスワードを更新"
@@ -4645,6 +4992,7 @@ msgstr "プロフィールを更新"
 msgid "Update successfully"
 msgstr "更新に成功しました"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4657,7 +5005,7 @@ msgstr "更新に成功しました"
 msgid "Updated at"
 msgstr "更新日時"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4787,10 +5135,10 @@ msgstr "閲覧済み"
 msgid "Waiting processes"
 msgstr "待機プロセス"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "警告"
 
@@ -4826,6 +5174,18 @@ msgstr "WebAuthn 設定が構成されていません"
 msgid "WebSocket connection error"
 msgstr "WebSocket接続エラー"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "水曜日"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "毎週"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "毎週%{day}の%{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4867,7 +5227,7 @@ msgstr "ワーカープロセス"
 msgid "Workers"
 msgstr "ワーカー"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "ワークスペース"
@@ -4945,6 +5305,9 @@ msgstr "以前のコードはもう使えません。"
 msgid "Your passkeys"
 msgstr "あなたのパスキー"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "最後のバックアップエラー"
+
 #~ msgid "Apply"
 #~ msgstr "適用"
 
@@ -4969,9 +5332,6 @@ msgstr "あなたのパスキー"
 #~ msgid "Ok"
 #~ msgstr "OK"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "必須項目を入力してください"
-
 #~ msgid "Recover"
 #~ msgstr "復元"
 

+ 383 - 23
app/src/language/ko_KR/app.po

@@ -106,7 +106,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "2FA 설정"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "대하여"
 
@@ -132,6 +132,7 @@ msgstr "ACME 사용자"
 msgid "Action"
 msgstr "작업"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -344,6 +345,11 @@ msgstr "자동"
 msgid "auto = CPU cores"
 msgstr "자동 = CPU 코어"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "자동 백업"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "자동 새로고침"
@@ -385,7 +391,7 @@ msgstr "홈으로"
 msgid "Back to List"
 msgstr "목록으로 돌아가기"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "백업"
 
@@ -397,10 +403,38 @@ msgstr "백업 파일 무결성 검사에 실패했습니다. 파일이 변조
 msgid "Backup file not found: {0}"
 msgstr "백업 파일을 찾을 수 없음: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "백업이 성공적으로 다운로드되었습니다"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "백업 경로"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "백업 경로가 존재하지 않습니다: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "백업 경로가 디렉터리가 아닙니다: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "사용자 정의 디렉터리 백업에는 백업 경로가 필요합니다"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "백업 경로가 허용된 접근 경로에 없습니다: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "백업 일정"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "백업 유형"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "차단 시간(분)"
@@ -456,6 +490,14 @@ msgstr "아래에는 일괄 수정하려는 선택된 항목이 있습니다"
 msgid "Block is nil"
 msgstr "블록이 nil입니다"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "두 구성 모두"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Nginx 및 Nginx UI 구성 모두"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "빌드 환경"
@@ -527,6 +569,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "취소"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "백업 경로 {0}에 접근할 수 없습니다: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "저장 경로 {0}에 접근할 수 없습니다: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "데모 모드에서는 초기 사용자 비밀번호를 변경할 수 없습니다"
@@ -952,12 +1002,12 @@ msgstr "내용"
 msgid "Copied"
 msgstr "복사됨"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "복사 완료!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "복사"
 
@@ -993,7 +1043,7 @@ msgstr "생성"
 msgid "Create Another"
 msgstr "다른 것 생성하기"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "백업 생성"
 
@@ -1005,12 +1055,13 @@ msgstr "파일 생성"
 msgid "Create Folder"
 msgstr "폴더 생성"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 msgstr "Nginx 구성 및 Nginx UI 설정을 포함한 시스템 백업을 생성합니다. 백업 파일은 자동으로 컴퓨터에 다운로드됩니다."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1035,6 +1086,10 @@ msgstr "인증 정보"
 msgid "Credentials"
 msgstr "인증 정보들"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron 표현식"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "현재 계정에 TOTP가 활성화되어 있습니다."
@@ -1068,17 +1123,42 @@ msgstr "현재 버전"
 msgid "Custom"
 msgstr "사용자 정의"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "사용자 정의 cron 표현식"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "사용자 지정 디렉토리"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "환경 표시기에 표시될 로컬 노드의 이름을 사용자 지정합니다."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "매일"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "매일 %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "대시보드"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "월 중 일자"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "요일"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "일"
@@ -1276,6 +1356,7 @@ msgstr "%{node}에서 스트림 %{name} 비활성화 실패"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "스트림 %{name}을(를) %{node}에서 비활성화했습니다"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1410,6 +1491,10 @@ msgstr "로컬로 성공적으로 복제됨"
 msgid "Dynamic"
 msgstr "동적"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "예: 0 0 * * * (매일 자정)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "편집"
@@ -1536,6 +1621,8 @@ msgstr "TLS 활성화"
 msgid "Enable TOTP"
 msgstr "TOTP 활성화"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1602,6 +1689,18 @@ msgstr "콘텐츠 처리 오류"
 msgid "Executable Path"
 msgstr "실행 가능 경로"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "매주 %{day} %{time}에 실행"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "매일 %{time}에 실행"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "매월 %{day}일 %{time}에 실행"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1628,6 +1727,11 @@ msgstr "외부 알림"
 msgid "Fail to obtain certificate"
 msgstr "인증서 획득 실패"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "실패"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "exec 인스턴스에 연결하지 못했습니다: {0}"
@@ -1680,6 +1784,10 @@ msgstr "Nginx 설정 디렉토리 복사 실패: {0}"
 msgid "Failed to create backup"
 msgstr "백업 생성 실패"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "백업 디렉터리 생성 실패: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "백업 파일 생성 실패: {0}"
@@ -1704,6 +1812,10 @@ msgstr "상위 디렉터리 생성 실패: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "복원 디렉토리 생성 실패: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "저장 디렉터리 생성 실패 {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "심볼릭 링크 생성 실패: {0}"
@@ -1916,6 +2028,10 @@ msgstr "임시 컨테이너 시작 실패: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "해시 확인 실패: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "백업 파일 쓰기 실패: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "복호화된 파일 쓰기 실패: {0}"
@@ -1924,6 +2040,10 @@ msgstr "복호화된 파일 쓰기 실패: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "암호화된 파일 쓰기 실패: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "보안 키 파일 쓰기 실패: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "ZIP 버퍼에 쓰기 실패: {0}"
@@ -1979,6 +2099,14 @@ msgstr "코드 형식"
 msgid "Format successfully"
 msgstr "형식화 성공"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "형식: 분 시 일 월 요일"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "금요일"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "일반 인증서"
@@ -2055,7 +2183,7 @@ msgstr "값이 높을수록 연결 재사용이 더 좋다는 것을 의미합
 msgid "History"
 msgstr "기록"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "홈"
 
@@ -2063,6 +2191,10 @@ msgstr "홈"
 msgid "Host"
 msgstr "호스트"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "시간"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2234,6 +2366,10 @@ msgstr "복호화된 데이터의 패딩이 유효하지 않습니다"
 msgid "Invalid passcode or recovery code"
 msgstr "잘못된 패스코드 또는 복구 코드"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "잘못된 경로: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "유효하지 않은 복구 코드"
@@ -2301,6 +2437,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark 사용자 지정"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "마지막 백업 상태"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "마지막 백업 시간"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "마지막 확인 시간"
@@ -2394,10 +2538,17 @@ msgstr "데이터를 불러오는 중..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "로컬"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "로컬 경로 (예: /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "위치"
@@ -2589,6 +2740,10 @@ msgstr "최소 여유 공간"
 msgid "Minimum free space in the cache directory"
 msgstr "캐시 디렉터리의 최소 여유 공간"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "분"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "분"
@@ -2622,11 +2777,24 @@ msgstr "모듈"
 msgid "Modules"
 msgstr "모듈"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "월요일"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "매월"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "매월 %{day}일 %{time}에"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "여러 줄 지시문"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2726,6 +2894,11 @@ msgstr "Nginx 설정에 sites-enabled가 포함되어 있지 않음"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx 설정에 stream-enabled가 포함되어 있지 않음"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Nginx 설정"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Nginx 설정 디렉터리가 설정되지 않았습니다"
@@ -2863,6 +3036,11 @@ msgstr "Nginx 이론적 최대 성능"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI가 이미 설치되었습니다"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Nginx UI 구성"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Nginx UI 설정이 복원되었습니다"
@@ -3034,6 +3212,7 @@ msgstr "오프라인"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3042,7 +3221,6 @@ msgstr "오프라인"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "확인"
 
@@ -3174,6 +3352,10 @@ msgstr "비밀번호가 일치하지 않습니다"
 msgid "Path"
 msgstr "경로"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "허용된 접근 경로에 없는 경로: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "경로: {0}은(는) nginx 설정 디렉터리: {1} 아래에 있지 않습니다"
@@ -3182,6 +3364,11 @@ msgstr "경로: {0}은(는) nginx 설정 디렉터리: {1} 아래에 있지 않
 msgid "Payload resource is nil"
 msgstr "페이로드 리소스가 nil입니다"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "대기 중"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "실행"
@@ -3247,6 +3434,10 @@ msgstr "백업 시 받은 보안 토큰을 입력해 주세요"
 msgid "Please fill all fields correctly"
 msgstr "모든 필드를 올바르게 작성해 주세요"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "필수 S3 구성 필드를 입력해 주세요"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3299,8 +3490,8 @@ msgstr "비밀번호를 입력해주세요!"
 msgid "Please input your username!"
 msgstr "사용자 이름을 입력해주세요!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "로그인해 주세요."
 
@@ -3312,7 +3503,7 @@ msgstr "아래의 시간 설정 단위는 모두 초 단위임을 유의해주
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "설치를 진행하기 전에 모든 문제를 해결해 주세요"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "이 보안 토큰을 저장해 두세요. 복원 시 필요합니다:"
 
@@ -3775,6 +3966,109 @@ msgstr "실행 모드"
 msgid "Running"
 msgstr "실행 중"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "S3 액세스 키 ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "S3 액세스 키 ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "S3 액세스 키 ID가 필요합니다"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3 버킷"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "S3 버킷 액세스 거부: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "S3 버킷이 필요합니다"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "S3 버킷 이름"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "S3 구성이 불완전합니다: {0} 누락됨"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "S3 연결 테스트 실패"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "S3 연결 테스트 실패: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "S3 연결 테스트 성공"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "S3 자격 증명이 유효하지 않습니다: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "S3 엔드포인트"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "S3 엔드포인트가 유효하지 않습니다: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "S3 엔드포인트가 필요합니다"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "S3 엔드포인트 URL"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "S3 경로 (예: backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "S3 리전"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "S3 리전 (예: us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "S3 비밀 액세스 키"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "S3 비밀 액세스 키"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "S3 비밀 액세스 키가 필요합니다"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "S3 업로드 실패: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "토요일"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3852,6 +4146,14 @@ msgstr "sbin 경로가 존재하지 않습니다"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "휴대폰으로 QR 코드를 스캔하여 앱에 계정을 추가하세요."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "일정"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "일정 유형"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3877,7 +4179,7 @@ msgstr "보안 설정"
 msgid "Security Token"
 msgstr "보안 토큰"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "보안 토큰 정보"
 
@@ -4132,6 +4434,29 @@ msgstr "정지됨"
 msgid "Storage"
 msgstr "저장소"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "스토리지 구성"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "저장 경로"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "저장 경로는 필수입니다"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "저장 경로가 허용된 접근 경로에 없습니다: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "저장소 유형"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "스트림이 활성화되었습니다"
@@ -4156,10 +4481,17 @@ msgstr "streams-enabled 디렉터리가 존재하지 않습니다"
 msgid "Stub Status Port"
 msgstr "스텁 상태 포트"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "성공"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "일요일"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4273,7 +4605,7 @@ msgstr "동기화"
 msgid "System"
 msgstr "시스템"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "시스템 백업"
 
@@ -4290,7 +4622,6 @@ msgid "System Restore"
 msgstr "시스템 복원"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "시스템이 성공적으로 복원되었습니다."
 
@@ -4314,6 +4645,10 @@ msgstr "터미널"
 msgid "Terminal Start Command"
 msgstr "터미널 시작 명령"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "S3 연결 테스트"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4470,7 +4805,7 @@ msgid ""
 "certificate files on the file system will not be deleted."
 msgstr "이 작업은 데이터베이스에서만 인증서를 제거합니다. 파일 시스템의 인증서 파일은 삭제되지 않습니다."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4502,6 +4837,10 @@ msgstr "이 작업은 %{nodeNames}의 Nginx UI를 %{version}으로 업그레이
 msgid "Throttle"
 msgstr "제한"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "목요일"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4610,6 +4949,10 @@ msgstr "TOTP는 시간 기반의 일회용 비밀번호 알고리즘을 사용
 msgid "Trash"
 msgstr "휴지통"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "화요일"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "2단계 인증이 필요합니다"
@@ -4626,6 +4969,10 @@ msgstr "유형"
 msgid "Unknown"
 msgstr "알 수 없음"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "지원되지 않는 백업 유형: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "비밀번호 업데이트"
@@ -4638,6 +4985,7 @@ msgstr "프로필 업데이트"
 msgid "Update successfully"
 msgstr "성공적으로 업데이트되었습니다"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4650,7 +4998,7 @@ msgstr "성공적으로 업데이트되었습니다"
 msgid "Updated at"
 msgstr "업데이트됨"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4780,10 +5128,10 @@ msgstr "확인됨"
 msgid "Waiting processes"
 msgstr "대기 과정"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "경고"
 
@@ -4819,6 +5167,18 @@ msgstr "WebAuthn 설정이 구성되지 않았습니다"
 msgid "WebSocket connection error"
 msgstr "WebSocket 연결 오류"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "수요일"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "매주"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "매주 %{day} %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4860,7 +5220,7 @@ msgstr "작업자 프로세스"
 msgid "Workers"
 msgstr "워커"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "작업 공간"
@@ -4940,6 +5300,9 @@ msgstr "이전 코드는 더 이상 작동하지 않습니다."
 msgid "Your passkeys"
 msgstr "귀하의 패스키"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "마지막 백업 오류"
+
 #~ msgid "Apply"
 #~ msgstr "적용"
 
@@ -4964,9 +5327,6 @@ msgstr "귀하의 패스키"
 #~ msgid "Ok"
 #~ msgstr "확인"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "필수 항목을 입력해 주세요"
-
 #~ msgid "Recover"
 #~ msgstr "복구"
 

+ 379 - 18
app/src/language/messages.pot

@@ -94,7 +94,7 @@ msgstr ""
 msgid "2FA Settings"
 msgstr ""
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr ""
 
@@ -121,6 +121,7 @@ msgstr ""
 msgid "Action"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -338,6 +339,11 @@ msgstr ""
 msgid "auto = CPU cores"
 msgstr ""
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr ""
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr ""
@@ -381,7 +387,8 @@ msgstr ""
 msgid "Back to List"
 msgstr ""
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11
+#: src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr ""
 
@@ -393,10 +400,38 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr ""
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr ""
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr ""
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr ""
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr ""
@@ -452,6 +487,14 @@ msgstr ""
 msgid "Block is nil"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr ""
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr ""
@@ -519,6 +562,14 @@ msgstr ""
 msgid "Cancel"
 msgstr ""
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr ""
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr ""
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr ""
@@ -897,12 +948,12 @@ msgstr ""
 msgid "Copied"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr ""
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr ""
 
@@ -938,7 +989,7 @@ msgstr ""
 msgid "Create Another"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr ""
 
@@ -951,10 +1002,11 @@ msgstr ""
 msgid "Create Folder"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid "Create system backups including Nginx configuration and Nginx UI settings. Backup files will be automatically downloaded to your computer."
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -979,6 +1031,10 @@ msgstr ""
 msgid "Credentials"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr ""
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr ""
@@ -1012,10 +1068,27 @@ msgstr ""
 msgid "Custom"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr ""
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid "Customize the name of local node to be displayed in the environment indicator."
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr ""
+
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161
@@ -1023,6 +1096,14 @@ msgstr ""
 msgid "Dashboard"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr ""
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr ""
@@ -1226,6 +1307,7 @@ msgstr ""
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1358,6 +1440,10 @@ msgstr ""
 msgid "Dynamic"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr ""
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr ""
@@ -1487,6 +1573,8 @@ msgstr ""
 msgid "Enable TOTP"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1555,6 +1643,18 @@ msgstr ""
 msgid "Executable Path"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr ""
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1581,6 +1681,11 @@ msgstr ""
 msgid "Fail to obtain certificate"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr ""
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr ""
@@ -1633,6 +1738,10 @@ msgstr ""
 msgid "Failed to create backup"
 msgstr ""
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr ""
@@ -1657,6 +1766,10 @@ msgstr ""
 msgid "Failed to create restore directory: {0}"
 msgstr ""
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr ""
@@ -1869,6 +1982,10 @@ msgstr ""
 msgid "Failed to verify hashes: {0}"
 msgstr ""
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr ""
@@ -1877,6 +1994,10 @@ msgstr ""
 msgid "Failed to write encrypted file: {0}"
 msgstr ""
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr ""
@@ -1931,6 +2052,14 @@ msgstr ""
 msgid "Format successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr ""
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr ""
@@ -2007,7 +2136,7 @@ msgstr ""
 msgid "History"
 msgstr ""
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr ""
 
@@ -2015,6 +2144,10 @@ msgstr ""
 msgid "Host"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr ""
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr ""
@@ -2177,6 +2310,10 @@ msgstr ""
 msgid "Invalid passcode or recovery code"
 msgstr ""
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr ""
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr ""
@@ -2242,6 +2379,14 @@ msgstr ""
 msgid "Lark Custom"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr ""
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr ""
@@ -2335,10 +2480,17 @@ msgstr ""
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr ""
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr ""
@@ -2522,6 +2674,10 @@ msgstr ""
 msgid "Minimum free space in the cache directory"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr ""
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr ""
@@ -2556,11 +2712,24 @@ msgstr ""
 msgid "Modules"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr ""
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr ""
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2662,6 +2831,11 @@ msgstr ""
 msgid "Nginx conf not include stream-enabled"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr ""
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr ""
@@ -2802,6 +2976,11 @@ msgstr ""
 msgid "Nginx UI already installed"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr ""
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr ""
@@ -2971,6 +3150,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110
 #: src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -2979,7 +3159,6 @@ msgstr ""
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr ""
 
@@ -3109,6 +3288,10 @@ msgstr ""
 msgid "Path"
 msgstr ""
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr ""
+
 #: src/constants/errors/cert.ts:7
 #: src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
@@ -3118,6 +3301,11 @@ msgstr ""
 msgid "Payload resource is nil"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr ""
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr ""
@@ -3179,6 +3367,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr ""
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid "Please fill in the API authentication credentials provided by your DNS provider."
 msgstr ""
@@ -3223,8 +3415,8 @@ msgstr ""
 msgid "Please input your username!"
 msgstr ""
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr ""
 
@@ -3236,7 +3428,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 
@@ -3694,6 +3886,109 @@ msgstr ""
 msgid "Running"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr ""
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr ""
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr ""
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr ""
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr ""
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr ""
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18
@@ -3774,6 +4069,14 @@ msgstr ""
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr ""
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr ""
@@ -3800,7 +4103,7 @@ msgstr ""
 msgid "Security Token"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr ""
 
@@ -4049,6 +4352,29 @@ msgstr ""
 msgid "Storage"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr ""
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr ""
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr ""
@@ -4074,10 +4400,17 @@ msgid "Stub Status Port"
 msgstr ""
 
 #: src/constants/index.ts:25
+#: src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
 #: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr ""
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid "Support communication with the backend through the Server-Sent Events protocol. If your Nginx UI is being used via an Nginx reverse proxy, please refer to this link to write the corresponding configuration file: https://nginxui.com/guide/nginx-proxy-example.html"
 msgstr ""
@@ -4183,7 +4516,7 @@ msgstr ""
 msgid "System"
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr ""
 
@@ -4200,7 +4533,6 @@ msgid "System Restore"
 msgstr ""
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr ""
 
@@ -4225,6 +4557,10 @@ msgstr ""
 msgid "Terminal Start Command"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr ""
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid "The certificate for the domain will be checked 30 minutes, and will be renewed if it has been more than 1 week or the period you set in settings since it was last issued."
 msgstr ""
@@ -4353,7 +4689,7 @@ msgstr ""
 msgid "This operation will only remove the certificate from the database. The certificate files on the file system will not be deleted."
 msgstr ""
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid "This token will only be shown once and cannot be retrieved later. Please make sure to save it in a secure location."
 msgstr ""
 
@@ -4380,6 +4716,10 @@ msgstr ""
 msgid "Throttle"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr ""
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4466,6 +4806,10 @@ msgstr ""
 msgid "Trash"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr ""
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr ""
@@ -4482,6 +4826,10 @@ msgstr ""
 msgid "Unknown"
 msgstr ""
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr ""
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr ""
@@ -4494,6 +4842,7 @@ msgstr ""
 msgid "Update successfully"
 msgstr ""
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35
@@ -4508,7 +4857,7 @@ msgstr ""
 msgid "Updated at"
 msgstr ""
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154
@@ -4643,10 +4992,10 @@ msgid "Waiting processes"
 msgstr ""
 
 #: src/constants/index.ts:23
+#: src/views/backup/components/BackupCreator.vue:138
 #: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr ""
 
@@ -4675,6 +5024,18 @@ msgstr ""
 msgid "WebSocket connection error"
 msgstr ""
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr ""
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr ""
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr ""
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid "When Enabled, Nginx UI will automatically re-register users upon startup. Generally, do not enable this unless you are in a dev environment and using Pebble as CA."
 msgstr ""
@@ -4708,7 +5069,7 @@ msgid "Workers"
 msgstr ""
 
 #: src/layouts/HeaderLayout.vue:62
-#: src/routes/index.ts:56
+#: src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr ""

+ 383 - 23
app/src/language/pt_PT/app.po

@@ -108,7 +108,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "Definições 2FA"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Sobre"
 
@@ -134,6 +134,7 @@ msgstr "Utilizador ACME"
 msgid "Action"
 msgstr "Acção"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -352,6 +353,11 @@ msgstr "Automático"
 msgid "auto = CPU cores"
 msgstr "auto = núcleos da CPU"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Backup automático"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Actualizar Automaticamente"
@@ -393,7 +399,7 @@ msgstr "Voltar ao Início"
 msgid "Back to List"
 msgstr "Voltar à lista"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Cópia de segurança"
 
@@ -407,10 +413,38 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr "Ficheiro de cópia de segurança não encontrado: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "O backup foi descarregado com sucesso"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Caminho de backup"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "O caminho de backup não existe: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "O caminho de backup não é um diretório: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "O caminho de backup é necessário para o backup de diretório personalizado"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Caminho de backup não está nos caminhos de acesso concedidos: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Agendamento de backup"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Tipo de backup"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Minutos Limite para Banir"
@@ -466,6 +500,14 @@ msgstr "Abaixo estão os itens selecionados que pretende modificar em lote"
 msgid "Block is nil"
 msgstr "O bloco é nulo"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Ambas configurações"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Configuração tanto do Nginx como da UI do Nginx"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Build com"
@@ -537,6 +579,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Cancelar"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Não é possível aceder ao caminho de backup {0}: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Não é possível aceder ao caminho de armazenamento {0}: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Não é possível alterar a senha do usuário inicial no modo de demonstração"
@@ -982,12 +1032,12 @@ msgstr "Conteúdo"
 msgid "Copied"
 msgstr "Copiado"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Copiado!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Copiar"
 
@@ -1025,7 +1075,7 @@ msgstr "Criar"
 msgid "Create Another"
 msgstr "Criar Outro"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Criar cópia de segurança"
 
@@ -1037,7 +1087,7 @@ msgstr "Criar Ficheiro"
 msgid "Create Folder"
 msgstr "Criar Pasta"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1046,6 +1096,7 @@ msgstr ""
 "definições do Nginx UI. Os ficheiros de backup serão transferidos "
 "automaticamente para o seu computador."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1070,6 +1121,10 @@ msgstr "Credencial"
 msgid "Credentials"
 msgstr "Credenciais"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Expressão Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "A conta atual tem o TOTP ativado."
@@ -1103,17 +1158,42 @@ msgstr "Versão Actual"
 msgid "Custom"
 msgstr "Personalizado"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Expressão cron personalizada"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Diretório personalizado"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "Personalize o nome do nó local a ser exibido no indicador de ambiente."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Diário"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Diariamente às %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Painel"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "Dia do mês"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "Dia da semana"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Dias"
@@ -1313,6 +1393,7 @@ msgstr "Falha ao desativar o fluxo %{name} de %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Desativar o fluxo %{name} de %{node} com sucesso"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1448,6 +1529,10 @@ msgstr "Duplicado para local com sucesso"
 msgid "Dynamic"
 msgstr "Dinâmico"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "ex., 0 0 * * * (diariamente à meia-noite)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Editar"
@@ -1574,6 +1659,8 @@ msgstr "Activar TLS"
 msgid "Enable TOTP"
 msgstr "Ativar TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1640,6 +1727,18 @@ msgstr "Erro ao processar o conteúdo"
 msgid "Executable Path"
 msgstr "Caminho Executável"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Executar todos os %{day} às %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Executar todos os dias às %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Executar todos os meses no dia %{day} às %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1666,6 +1765,11 @@ msgstr "Notificação Externa"
 msgid "Fail to obtain certificate"
 msgstr "Obtenção de Certificado Falhou"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Falhou"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Falha ao anexar à instância de execução: {0}"
@@ -1718,6 +1822,10 @@ msgstr "Falha ao copiar o diretório de configuração do Nginx: {0}"
 msgid "Failed to create backup"
 msgstr "Falha ao criar cópia de segurança"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Falha ao criar o diretório de backup: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Falha ao criar o ficheiro de backup: {0}"
@@ -1742,6 +1850,10 @@ msgstr "Falha ao criar o diretório principal: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Falha ao criar o diretório de restauro: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Falha ao criar o diretório de armazenamento {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Falha ao criar ligação simbólica: {0}"
@@ -1954,6 +2066,10 @@ msgstr "Falha ao iniciar o contentor temporário: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Falha ao verificar hashes: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Falha ao escrever o arquivo de backup: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Falha ao escrever o ficheiro desencriptado: {0}"
@@ -1962,6 +2078,10 @@ msgstr "Falha ao escrever o ficheiro desencriptado: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Falha ao escrever o ficheiro encriptado: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Falha ao escrever o ficheiro da chave de segurança: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Falha ao escrever no buffer ZIP: {0}"
@@ -2019,6 +2139,14 @@ msgstr "Formatar Código"
 msgid "Format successfully"
 msgstr "Formatado com Sucesso"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Formato: minuto hora dia mês dia_da_semana"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Sexta-feira"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Certificado Geral"
@@ -2095,7 +2223,7 @@ msgstr "Um valor mais alto significa uma melhor reutilização da conexão"
 msgid "History"
 msgstr "Histórico"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Início"
 
@@ -2103,6 +2231,10 @@ msgstr "Início"
 msgid "Host"
 msgstr "Host"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Hora"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2285,6 +2417,10 @@ msgstr "Preenchimento inválido nos dados desencriptados"
 msgid "Invalid passcode or recovery code"
 msgstr "Passcode ou código de recuperação inválido"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Caminho inválido: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Código de recuperação inválido"
@@ -2354,6 +2490,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Personalizado"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Estado do último backup"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Hora do último backup"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Última verificação em"
@@ -2447,10 +2591,17 @@ msgstr "A carregar dados..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Local"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Caminho local (ex., /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Localização"
@@ -2649,6 +2800,10 @@ msgstr "Espaço livre mínimo"
 msgid "Minimum free space in the cache directory"
 msgstr "Espaço livre mínimo no diretório de cache"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Minuto"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Minutos"
@@ -2682,11 +2837,24 @@ msgstr "Módulo"
 msgid "Modules"
 msgstr "Módulos"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Segunda-feira"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Mensal"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Mensalmente no dia %{day} às %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Diretiva Multilinha"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2786,6 +2954,11 @@ msgstr "A configuração do Nginx não inclui sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "A configuração do Nginx não inclui stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Configuração do Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "O diretório de configuração do Nginx não está definido"
@@ -2923,6 +3096,11 @@ msgstr "Desempenho teórico máximo do Nginx"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI já está instalado"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Configuração da interface do Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "A configuração do Nginx UI foi restaurada"
@@ -3102,6 +3280,7 @@ msgstr "Off-line"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3110,7 +3289,6 @@ msgstr "Off-line"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "OK"
 
@@ -3244,6 +3422,10 @@ msgstr "As palavras-passe não coincidem"
 msgid "Path"
 msgstr "Caminho"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Caminho não está nos caminhos de acesso concedidos: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "O caminho: {0} não está no diretório de configuração do nginx: {1}"
@@ -3252,6 +3434,11 @@ msgstr "O caminho: {0} não está no diretório de configuração do nginx: {1}"
 msgid "Payload resource is nil"
 msgstr "O recurso de carga útil é nulo"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "Pendente"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Realizar"
@@ -3321,6 +3508,10 @@ msgstr "Por favor, insira o token de segurança recebido durante a cópia de seg
 msgid "Please fill all fields correctly"
 msgstr "Por favor, preencha todos os campos corretamente"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Por favor, preencha os campos de configuração do S3 necessários"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3383,8 +3574,8 @@ msgstr "Por favor introduza a sua palavra-passe!"
 msgid "Please input your username!"
 msgstr "Por favor introduza o seu nome de utilizador!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Por favor, faça login."
 
@@ -3396,7 +3587,7 @@ msgstr "Note que as definições da unidade de tempo abaixo estão todas em segu
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Por favor, resolva todos os problemas antes de prosseguir com a instalação"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Por favor, guarde este token de segurança, vai precisar dele para a "
@@ -3870,6 +4061,109 @@ msgstr "Modo de Execução"
 msgid "Running"
 msgstr "Executando"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "ID da chave de acesso S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "ID da chave de acesso S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "O ID da chave de acesso S3 é obrigatório"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "Balde S3"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Acesso ao bucket S3 negado: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "O bucket S3 é obrigatório"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "Nome do bucket S3"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "A configuração do S3 está incompleta: falta {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "Teste de conexão S3 falhou"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "Teste de conexão S3 falhou: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "Teste de conexão S3 bem-sucedido"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "As credenciais do S3 são inválidas: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "Ponto de extremidade S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "O endpoint S3 é inválido: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "O endpoint S3 é obrigatório"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "URL do endpoint S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "Caminho S3 (ex., backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "Região S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "Região S3 (ex., us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "Chave de acesso secreta do S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "Chave de Acesso Secreto do S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "A chave de acesso secreta do S3 é necessária"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "Falha no upload para S3: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Sábado"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3949,6 +4243,14 @@ msgstr ""
 "Digitalize o código QR com o seu telemóvel para adicionar a conta à "
 "aplicação."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Agendamento"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Tipo de agendamento"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3974,7 +4276,7 @@ msgstr "Configurações de segurança"
 msgid "Security Token"
 msgstr "Token de segurança"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Informações do token de segurança"
 
@@ -4237,6 +4539,29 @@ msgstr "Parado"
 msgid "Storage"
 msgstr "Armazenamento"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Configuração de armazenamento"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Caminho de armazenamento"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "O caminho de armazenamento é obrigatório"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Caminho de armazenamento não está nos caminhos de acesso concedidos: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Tipo de armazenamento"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "O fluxo está ativado"
@@ -4261,10 +4586,17 @@ msgstr "O diretório streams-enabled não existe"
 msgid "Stub Status Port"
 msgstr "Porta de estado stub"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Sucesso"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Domingo"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4380,7 +4712,7 @@ msgstr "Sincronização"
 msgid "System"
 msgstr "Sistema"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Cópia de segurança do sistema"
 
@@ -4397,7 +4729,6 @@ msgid "System Restore"
 msgstr "Restauro do sistema"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Sistema restaurado com sucesso."
 
@@ -4421,6 +4752,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgstr "Comando de Inicialização do Terminal"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Testar conexão S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4600,7 +4935,7 @@ msgstr ""
 "Esta operação irá apenas remover o certificado da base de dados. Os "
 "ficheiros do certificado no sistema de ficheiros não serão eliminados."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4640,6 +4975,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Limitação"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Quinta-feira"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4758,6 +5097,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Lixo"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Terça-feira"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Autenticação de dois fatores necessária"
@@ -4774,6 +5117,10 @@ msgstr "Tipo"
 msgid "Unknown"
 msgstr "Desconhecido"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Tipo de backup não suportado: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Atualizar senha"
@@ -4786,6 +5133,7 @@ msgstr "Atualizar perfil"
 msgid "Update successfully"
 msgstr "Atualização bem-sucedida"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4798,7 +5146,7 @@ msgstr "Atualização bem-sucedida"
 msgid "Updated at"
 msgstr "Actualizado em"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4928,10 +5276,10 @@ msgstr "Visualizado"
 msgid "Waiting processes"
 msgstr "Processos em espera"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Aviso"
 
@@ -4974,6 +5322,18 @@ msgstr "As configurações do WebAuthn não estão configuradas"
 msgid "WebSocket connection error"
 msgstr "Erro de conexão WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "quarta-feira"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Semanal"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Semanalmente no %{day} às %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5020,7 +5380,7 @@ msgstr "Processos de trabalho"
 msgid "Workers"
 msgstr "Workers"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Espaço de Trabalho"
@@ -5107,6 +5467,9 @@ msgstr "Os seus códigos antigos não funcionarão mais."
 msgid "Your passkeys"
 msgstr "As suas chaves de acesso"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Último erro de backup"
+
 #~ msgid "Apply"
 #~ msgstr "Aplicar"
 
@@ -5131,9 +5494,6 @@ msgstr "As suas chaves de acesso"
 #~ msgid "Ok"
 #~ msgstr "Ok"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Por favor preencha os campos obrigatórios"
-
 #~ msgid "Recover"
 #~ msgstr "Recuperar"
 

+ 385 - 23
app/src/language/ru_RU/app.po

@@ -112,7 +112,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "Настройки 2FA"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "О проекте"
 
@@ -138,6 +138,7 @@ msgstr "Пользователь ACME"
 msgid "Action"
 msgstr "Действие"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -354,6 +355,11 @@ msgstr "Авто"
 msgid "auto = CPU cores"
 msgstr "Auto = ядра процессора"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Автоматическое резервное копирование"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Автообновление"
@@ -395,7 +401,7 @@ msgstr "Вернуться на главную"
 msgid "Back to List"
 msgstr "Вернуться к списку"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Резервная копия"
 
@@ -409,10 +415,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgstr "Файл резервной копии не найден: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "Резервная копия успешно загружена"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Путь резервной копии"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "Путь для резервного копирования не существует: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "Путь резервного копирования не является каталогом: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+"Путь резервного копирования требуется для резервного копирования "
+"пользовательского каталога"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Путь резервного копирования не входит в разрешенные пути доступа: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Расписание резервного копирования"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Тип резервной копии"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Порог блокировки в минутах"
@@ -468,6 +504,14 @@ msgstr "Ниже приведены выбранные элементы, кот
 msgid "Block is nil"
 msgstr "Блок равен nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Обе конфигурации"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Конфигурация и Nginx, и Nginx UI"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Собрать с"
@@ -541,6 +585,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Отмена"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Невозможно получить доступ к пути резервного копирования {0}: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Невозможно получить доступ к пути хранения {0}: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Невозможно изменить пароль начального пользователя в демо-режиме"
@@ -983,12 +1035,12 @@ msgstr "Содержание"
 msgid "Copied"
 msgstr "Скопировано"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Скопировано!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Копировать"
 
@@ -1026,7 +1078,7 @@ msgstr "Создать"
 msgid "Create Another"
 msgstr "Создать еще"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Создать резервную копию"
 
@@ -1038,7 +1090,7 @@ msgstr "Создать файл"
 msgid "Create Folder"
 msgstr "Создать папку"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1047,6 +1099,7 @@ msgstr ""
 "Nginx UI. Файлы резервных копий будут автоматически загружены на ваш "
 "компьютер."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1071,6 +1124,10 @@ msgstr "Учетные данные"
 msgid "Credentials"
 msgstr "Учетные данные"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Выражение Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "Текущая учетная запись имеет включенную TOTP."
@@ -1104,17 +1161,42 @@ msgstr "Текущяя версия"
 msgid "Custom"
 msgstr "Пользовательский"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Пользовательское cron-выражение"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Пользовательская директория"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "Настройте имя локального узла для отображения в индикаторе среды."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Ежедневно"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Ежедневно в %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Доска"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "День месяца"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "День недели"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Дни"
@@ -1312,6 +1394,7 @@ msgstr "Не удалось отключить поток %{name} с узла %{
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Поток %{name} отключен от %{node} успешно"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1447,6 +1530,10 @@ msgstr "Успешно дублировано на локальный"
 msgid "Dynamic"
 msgstr "Динамический"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "напр., 0 0 * * * (ежедневно в полночь)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Редактировать"
@@ -1573,6 +1660,8 @@ msgstr "Включить TLS"
 msgid "Enable TOTP"
 msgstr "Включить TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1639,6 +1728,18 @@ msgstr "Ошибка обработки содержимого"
 msgid "Executable Path"
 msgstr "Исполняемый путь"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Выполнять каждый %{day} в %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Выполнять каждый день в %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Выполнять каждый месяц в день %{day} в %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1665,6 +1766,11 @@ msgstr "Внешнее уведомление"
 msgid "Fail to obtain certificate"
 msgstr "Не удалось получить сертификат"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Не удалось"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Не удалось подключиться к экземпляру выполнения: {0}"
@@ -1717,6 +1823,10 @@ msgstr "Не удалось скопировать каталог конфигу
 msgid "Failed to create backup"
 msgstr "Не удалось создать резервную копию"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Не удалось создать каталог резервной копии: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Не удалось создать файл резервной копии: {0}"
@@ -1741,6 +1851,10 @@ msgstr "Не удалось создать родительский катало
 msgid "Failed to create restore directory: {0}"
 msgstr "Не удалось создать каталог восстановления: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Не удалось создать каталог хранилища {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Не удалось создать символическую ссылку: {0}"
@@ -1953,6 +2067,10 @@ msgstr "Не удалось запустить временный контейн
 msgid "Failed to verify hashes: {0}"
 msgstr "Не удалось проверить хеши: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Не удалось записать файл резервной копии: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Ошибка записи расшифрованного файла: {0}"
@@ -1961,6 +2079,10 @@ msgstr "Ошибка записи расшифрованного файла: {0}
 msgid "Failed to write encrypted file: {0}"
 msgstr "Ошибка записи зашифрованного файла: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Не удалось записать файл ключа безопасности: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Ошибка записи в ZIP-буфер: {0}"
@@ -2018,6 +2140,14 @@ msgstr "Форматировать код"
 msgid "Format successfully"
 msgstr "Форматирование успешно"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Формат: минута час день месяц день_недели"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Пятница"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Общий сертификат"
@@ -2094,7 +2224,7 @@ msgstr "Более высокое значение означает лучшее
 msgid "History"
 msgstr "История"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Главная"
 
@@ -2102,6 +2232,10 @@ msgstr "Главная"
 msgid "Host"
 msgstr "Хост"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Час"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2282,6 +2416,10 @@ msgstr "Некорректное заполнение в расшифрован
 msgid "Invalid passcode or recovery code"
 msgstr "Неверный пароль или код восстановления"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Неверный путь: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Неверный код восстановления"
@@ -2351,6 +2489,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Пользовательский"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Статус последнего резервного копирования"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Время последнего резервного копирования"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Последняя проверка в"
@@ -2444,10 +2590,17 @@ msgstr "Загрузка данных..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Локальный"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Локальный путь (напр., /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Локация"
@@ -2646,6 +2799,10 @@ msgstr "Минимальное свободное пространство"
 msgid "Minimum free space in the cache directory"
 msgstr "Минимальное свободное место в кэш-директории"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Минута"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Минуты"
@@ -2679,11 +2836,24 @@ msgstr "Модуль"
 msgid "Modules"
 msgstr "Модули"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Понедельник"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Ежемесячно"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Ежемесячно в день %{day} в %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Многострочная директива"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2783,6 +2953,11 @@ msgstr "Конфигурация Nginx не включает sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Конфигурация Nginx не включает stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Конфигурация Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Каталог конфигурации Nginx не задан"
@@ -2920,6 +3095,11 @@ msgstr "Теоретическая максимальная производит
 msgid "Nginx UI already installed"
 msgstr "Nginx UI уже установлен"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Конфигурация интерфейса Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Конфигурация Nginx UI была восстановлена"
@@ -3099,6 +3279,7 @@ msgstr "Оффлайн"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3107,7 +3288,6 @@ msgstr "Оффлайн"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "ОК"
 
@@ -3241,6 +3421,10 @@ msgstr "Пароли не совпадают"
 msgid "Path"
 msgstr "Путь"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Путь не входит в разрешенные пути доступа: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Путь: {0} не находится в каталоге конфигурации nginx: {1}"
@@ -3249,6 +3433,11 @@ msgstr "Путь: {0} не находится в каталоге конфигу
 msgid "Payload resource is nil"
 msgstr "Ресурс полезной нагрузки равен nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "В ожидании"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Выполнить"
@@ -3320,6 +3509,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgstr "Пожалуйста, заполните все поля правильно"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Пожалуйста, заполните обязательные поля конфигурации S3"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3383,8 +3576,8 @@ msgstr "Введите ваш пароль!"
 msgid "Please input your username!"
 msgstr "Введите ваше имя пользователя!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Пожалуйста, войдите в систему."
 
@@ -3398,7 +3591,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Пожалуйста, устраните все проблемы перед продолжением установки"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Пожалуйста, сохраните этот токен безопасности, он понадобится для "
@@ -3870,6 +4063,109 @@ msgstr "Режим работы"
 msgid "Running"
 msgstr "Выполняется"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "Идентификатор ключа доступа S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "Идентификатор ключа доступа S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "Требуется идентификатор ключа доступа S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3-бакет"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Доступ к корзине S3 запрещен: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "Требуется S3-бакет"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "Имя S3-бакета"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "Конфигурация S3 неполная: отсутствует {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "Тест подключения S3 не удался"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "Тест подключения S3 не удался: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "Тест подключения S3 успешно выполнен"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "Учетные данные S3 недействительны: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "Конечная точка S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "Недопустимая конечная точка S3: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "Требуется конечная точка S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "URL конечной точки S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "Путь S3 (напр., backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "Регион S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "Регион S3 (например, us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "Секретный ключ доступа S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "Секретный ключ доступа S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "Требуется секретный ключ доступа S3"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "Ошибка загрузки в S3: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Суббота"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3949,6 +4245,14 @@ msgstr ""
 "Отсканируйте QR-код с помощью мобильного телефона, чтобы добавить учетную "
 "запись в приложение."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Расписание"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Тип расписания"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3974,7 +4278,7 @@ msgstr "Настройки безопасности"
 msgid "Security Token"
 msgstr "Токен безопасности"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Информация о токене безопасности"
 
@@ -4231,6 +4535,29 @@ msgstr "Остановлен"
 msgid "Storage"
 msgstr "Хранилище"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Конфигурация хранилища"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Путь к хранилищу"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "Требуется путь к хранилищу"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Путь хранения не входит в предоставленные пути доступа: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Тип хранилища"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Поток включен"
@@ -4255,10 +4582,17 @@ msgstr "Каталог streams-enabled не существует"
 msgid "Stub Status Port"
 msgstr "Порт состояния заглушки"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Успех"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Воскресенье"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4374,7 +4708,7 @@ msgstr "Синхронизация"
 msgid "System"
 msgstr "Система"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Резервное копирование системы"
 
@@ -4391,7 +4725,6 @@ msgid "System Restore"
 msgstr "Восстановление системы"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Система успешно восстановлена."
 
@@ -4415,6 +4748,10 @@ msgstr "Терминал"
 msgid "Terminal Start Command"
 msgstr "Терминальная команда запуска"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Проверить подключение S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4595,7 +4932,7 @@ msgstr ""
 "Эта операция только удалит сертификат из базы данных. Файлы сертификата в "
 "файловой системе не будут удалены."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4635,6 +4972,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Ограничение"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Четверг"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4753,6 +5094,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Корзина"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Вторник"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Требуется двухфакторная аутентификация"
@@ -4769,6 +5114,10 @@ msgstr "Тип"
 msgid "Unknown"
 msgstr "Неизвестно"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Неподдерживаемый тип резервного копирования: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Обновить пароль"
@@ -4781,6 +5130,7 @@ msgstr "Обновить профиль"
 msgid "Update successfully"
 msgstr "Успешно обновлено"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4793,7 +5143,7 @@ msgstr "Успешно обновлено"
 msgid "Updated at"
 msgstr "Обновлено в"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4923,10 +5273,10 @@ msgstr "Просмотрено"
 msgid "Waiting processes"
 msgstr "Процессы ожидания"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Внимание"
 
@@ -4969,6 +5319,18 @@ msgstr "Настройки WebAuthn не настроены"
 msgid "WebSocket connection error"
 msgstr "Ошибка подключения WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "среда"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Еженедельно"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Еженедельно по %{day} в %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5015,7 +5377,7 @@ msgstr "Рабочие процессы"
 msgid "Workers"
 msgstr "Рабочие процессы"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Рабочее пространство"
@@ -5101,6 +5463,9 @@ msgstr "Ваши старые коды больше не будут работа
 msgid "Your passkeys"
 msgstr "Ваши ключи доступа"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Ошибка последнего резервного копирования"
+
 #~ msgid "Apply"
 #~ msgstr "Применить"
 
@@ -5125,9 +5490,6 @@ msgstr "Ваши ключи доступа"
 #~ msgid "Ok"
 #~ msgstr "Ок"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Пожалуйста, заполните обязательные поля"
-
 #~ msgid "Recover"
 #~ msgstr "Восстановить"
 

+ 383 - 23
app/src/language/tr_TR/app.po

@@ -108,7 +108,7 @@ msgstr "İki aşamalı kimlik doğrulaması(2FA)"
 msgid "2FA Settings"
 msgstr "2FA Ayarları"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Hakkında"
 
@@ -134,6 +134,7 @@ msgstr "ACME Kullanıcısı"
 msgid "Action"
 msgstr "Eylem"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -350,6 +351,11 @@ msgstr "Otomobil"
 msgid "auto = CPU cores"
 msgstr "Auto = CPU Çekirdekleri"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Otomatik Yedekleme"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Otomatik Yenileme"
@@ -391,7 +397,7 @@ msgstr "Ana Sayfaya Dön"
 msgid "Back to List"
 msgstr "Listeye Dön"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Yedekleme"
 
@@ -403,10 +409,38 @@ msgstr "Yedek dosya bütünlük kontrolü başarısız oldu, dosya değiştirilm
 msgid "Backup file not found: {0}"
 msgstr "Yedek dosya bulunamadı: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "Yedek başarıyla indirildi"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Yedekleme Yolu"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "Yedekleme yolu mevcut değil: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "Yedekleme yolu bir dizin değil: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "Özel dizin yedeklemesi için yedekleme yolu gereklidir"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Yedekleme yolu, verilen erişim yollarında değil: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Yedekleme Zamanlaması"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Yedekleme Türü"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Yasaklama Eşiği Süresi (Dakika)"
@@ -462,6 +496,14 @@ msgstr "Aşağıda toplu olarak değiştirmek istediğiniz seçili öğeler bulu
 msgid "Block is nil"
 msgstr "Blok nil değerinde"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Her İki Yapılandırma"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Hem Nginx hem de Nginx UI Yapılandırması"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "İle Oluşturuldu"
@@ -533,6 +575,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "İptal"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Yedekleme yolu {0} erişilemiyor: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Depolama yolu {0} erişilemiyor: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Demo modunda başlangıç kullanıcı şifresi değiştirilemez"
@@ -978,12 +1028,12 @@ msgstr "İçerik"
 msgid "Copied"
 msgstr "Kopyalandı"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Kopyalandı!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Kopya"
 
@@ -1021,7 +1071,7 @@ msgstr "Oluştur"
 msgid "Create Another"
 msgstr "Bir Başka Oluştur"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Yedek Oluştur"
 
@@ -1033,7 +1083,7 @@ msgstr "Dosya Oluştur"
 msgid "Create Folder"
 msgstr "Klasör Ekle"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1041,6 +1091,7 @@ msgstr ""
 "Nginx yapılandırması ve Nginx UI ayarlarını içeren sistem yedekleri "
 "oluşturun. Yedek dosyaları otomatik olarak bilgisayarınıza indirilecektir."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1065,6 +1116,10 @@ msgstr "Kimlik bilgisi"
 msgid "Credentials"
 msgstr "Kimlik bilgileri"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron İfadesi"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "Mevcut hesap için TOTP etkinleştirildi."
@@ -1098,17 +1153,42 @@ msgstr "Mevcut sürüm"
 msgid "Custom"
 msgstr "Özelleştirilmiş"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Özel cron ifadesi"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Özel Dizin"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "Ortam göstergesinde görüntülenecek yerel düğüm adını özelleştirin."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Günlük"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Günlük olarak saat %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Kontrol Paneli"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "Ayın Günü"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "Haftanın Günü"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Günler"
@@ -1308,6 +1388,7 @@ msgstr "%{node} üzerindeki %{name} akışı devre dışı bırakılamadı"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Akış %{name}, %{node} üzerinden başarıyla devre dışı bırakıldı"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1445,6 +1526,10 @@ msgstr "Başarıyla yerel kopya oluşturuldu"
 msgid "Dynamic"
 msgstr "Dinamik"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "örn., 0 0 * * * (her gece yarısı)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Düzenle"
@@ -1571,6 +1656,8 @@ msgstr "TLS'yi Etkinleştir"
 msgid "Enable TOTP"
 msgstr "TOTP'yi Etkinleştir"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1637,6 +1724,18 @@ msgstr "İçerik işlenirken hata"
 msgid "Executable Path"
 msgstr "Yürütülebilir Dosya Yolu"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Her %{day} %{time} saatinde çalıştır"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Her gün %{time} saatinde çalıştır"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Her ayın %{day} günü %{time} saatinde çalıştır"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1663,6 +1762,11 @@ msgstr "Harici Bildirim"
 msgid "Fail to obtain certificate"
 msgstr "Sertifika alınamadı"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Başarısız"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Yürütme örneğine bağlanılamadı: {0}"
@@ -1715,6 +1819,10 @@ msgstr "Nginx yapılandırma dizini kopyalanamadı: {0}"
 msgid "Failed to create backup"
 msgstr "Yedek oluşturma başarısız oldu"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Yedek dizini oluşturulamadı: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Yedek dosyası oluşturulamadı: {0}"
@@ -1739,6 +1847,10 @@ msgstr "Üst dizin oluşturulamadı: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Geri yükleme dizini oluşturulamadı: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Depolama dizini oluşturulamadı {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Sembolik bağlantı oluşturulamadı: {0}"
@@ -1951,6 +2063,10 @@ msgstr "Geçici konteyner başlatılamadı: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Hash doğrulama başarısız: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Yedek dosyası yazılamadı: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Şifresi çözülmüş dosya yazılamadı: {0}"
@@ -1959,6 +2075,10 @@ msgstr "Şifresi çözülmüş dosya yazılamadı: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Şifrelenmiş dosya yazma hatası: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Güvenlik anahtarı dosyası yazılamadı: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "ZIP tamponuna yazma başarısız: {0}"
@@ -2016,6 +2136,14 @@ msgstr "Kodu Biçimlendir"
 msgid "Format successfully"
 msgstr "Başarıyla biçimlendirildi"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Biçim: dakika saat gün ay haftanın_günü"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Cuma"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Genel Sertifika"
@@ -2092,7 +2220,7 @@ msgstr "Daha yüksek bir değer, daha iyi bağlantı yeniden kullanımı anlamı
 msgid "History"
 msgstr "Geçmiş"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Anasayfa"
 
@@ -2100,6 +2228,10 @@ msgstr "Anasayfa"
 msgid "Host"
 msgstr "Host"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Saat"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2284,6 +2416,10 @@ msgstr "Şifresi çözülmüş veride geçersiz dolgu"
 msgid "Invalid passcode or recovery code"
 msgstr "Geçersiz parola veya kurtarma kodu"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Geçersiz yol: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Geçersiz kurtarma kodu"
@@ -2353,6 +2489,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Özel"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Son Yedekleme Durumu"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Son Yedekleme Zamanı"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "En son şu tarihte kontrol edildi"
@@ -2446,10 +2590,17 @@ msgstr "Veriler yükleniyor..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Yerel"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Yerel yol (örn., /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Konum"
@@ -2647,6 +2798,10 @@ msgstr "Minimum Boş Alan"
 msgid "Minimum free space in the cache directory"
 msgstr "Önbellek dizinindeki minimum boş alan"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Dakika"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Dakika"
@@ -2680,11 +2835,24 @@ msgstr "Modül"
 msgid "Modules"
 msgstr "Modüller"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Pazartesi"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Aylık"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Her ayın %{day} günü %{time} saatinde"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Çok Satırlı Yönergeler"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2784,6 +2952,11 @@ msgstr "Nginx yapılandırması sites-enabled içermiyor"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx yapılandırması stream-enabled içermiyor"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Nginx Yapılandırması"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Nginx yapılandırma dizini ayarlanmamış"
@@ -2921,6 +3094,11 @@ msgstr "Nginx teorik maksimum performansı"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI zaten yüklü"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Nginx Arayüz Yapılandırması"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Nginx UI yapılandırması geri yüklendi"
@@ -3100,6 +3278,7 @@ msgstr "Çevrimdışı"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3108,7 +3287,6 @@ msgstr "Çevrimdışı"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "Tamam"
 
@@ -3241,6 +3419,10 @@ msgstr "Şifreler eşleşmiyor"
 msgid "Path"
 msgstr "Yol"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Yol, verilen erişim yollarında değil: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Yol: {0}, nginx yapılandırma dizini: {1} altında değil"
@@ -3249,6 +3431,11 @@ msgstr "Yol: {0}, nginx yapılandırma dizini: {1} altında değil"
 msgid "Payload resource is nil"
 msgstr "Yük kaynağı nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "Beklemede"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Uygula"
@@ -3318,6 +3505,10 @@ msgstr "Lütfen yedekleme sırasında alınan güvenlik belirtecini girin"
 msgid "Please fill all fields correctly"
 msgstr "Lütfen tüm alanları doğru şekilde doldurun"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Lütfen gerekli S3 yapılandırma alanlarını doldurun"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3379,8 +3570,8 @@ msgstr "Lütfen şifrenizi girin!"
 msgid "Please input your username!"
 msgstr "Lütfen kullanıcı adınızı girin!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Lütfen giriş yapın."
 
@@ -3394,7 +3585,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Lütfen kuruluma devam etmeden önce tüm sorunları çözün"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Lütfen bu güvenlik belirtecini kaydedin, geri yükleme için ihtiyacınız "
@@ -3875,6 +4066,109 @@ msgstr "Çalışma Modu"
 msgid "Running"
 msgstr "Koşma"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "S3 erişim anahtar kimliği"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "S3 Erişim Anahtar Kimliği"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "S3 erişim anahtar kimliği gereklidir"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3 Kovası"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "S3 kovası erişimi reddedildi: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "S3 kovası gereklidir"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "S3 kova adı"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "S3 yapılandırması eksik: {0} eksik"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "S3 bağlantı testi başarısız oldu"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "S3 bağlantı testi başarısız oldu: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "S3 bağlantı testi başarılı"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "S3 kimlik bilgileri geçersiz: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "S3 Uç Noktası"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "S3 uç noktası geçersiz: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "S3 uç noktası gereklidir"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "S3 uç nokta URL'si"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "S3 yolu (örn., backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "S3 Bölgesi"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "S3 bölgesi (örn., us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "S3 gizli erişim anahtarı"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "S3 Gizli Erişim Anahtarı"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "S3 gizli erişim anahtarı gereklidir"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "S3 yükleme başarısız: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Cumartesi"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3952,6 +4246,14 @@ msgstr "Sbin yolu mevcut değil"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "Hesabı uygulamaya eklemek için telefonunuzla QR kodunu tarayın."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Zamanlama"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Zamanlama Türü"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3977,7 +4279,7 @@ msgstr "Güvenlik Ayarları"
 msgid "Security Token"
 msgstr "Güvenlik Belirteci"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Güvenlik Belirteci Bilgileri"
 
@@ -4234,6 +4536,29 @@ msgstr "Durduruldu"
 msgid "Storage"
 msgstr "Depolama"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Depolama Yapılandırması"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Depolama Yolu"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "Depolama yolu gereklidir"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Depolama yolu verilen erişim yollarında değil: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Depolama Türü"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Yayın etkinleştirildi"
@@ -4258,10 +4583,17 @@ msgstr "Streams-enabled dizini mevcut değil"
 msgid "Stub Status Port"
 msgstr "Stub Durum Portu"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Başarı"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Pazar"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4379,7 +4711,7 @@ msgstr "Senkronizasyon"
 msgid "System"
 msgstr "Sistem"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Sistem Yedekleme"
 
@@ -4396,7 +4728,6 @@ msgid "System Restore"
 msgstr "Sistem Geri Yükleme"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Sistem başarıyla geri yüklendi."
 
@@ -4420,6 +4751,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgstr "Terminal Başlatma Komutu"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "S3 Bağlantısını Test Et"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4599,7 +4934,7 @@ msgstr ""
 "Bu işlem yalnızca sertifikayı veritabanından kaldıracaktır. Dosya "
 "sistemindeki sertifika dosyaları silinmeyecektir."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4639,6 +4974,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Kısıtlama"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Perşembe"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4757,6 +5096,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Çöp"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Salı"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "İki faktörlü kimlik doğrulama gerekiyor"
@@ -4773,6 +5116,10 @@ msgstr "Tür"
 msgid "Unknown"
 msgstr "Bilinmeyen"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Desteklenmeyen yedekleme türü: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Şifreyi Güncelle"
@@ -4785,6 +5132,7 @@ msgstr "Profili Güncelle"
 msgid "Update successfully"
 msgstr "Başarıyla güncellendi"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4797,7 +5145,7 @@ msgstr "Başarıyla güncellendi"
 msgid "Updated at"
 msgstr "Güncellenme Tarihi"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4927,10 +5275,10 @@ msgstr "Görüntülendi"
 msgid "Waiting processes"
 msgstr "Bekleme süreçleri"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Uyarı"
 
@@ -4973,6 +5321,18 @@ msgstr "WebAuthn ayarları yapılandırılmamış"
 msgid "WebSocket connection error"
 msgstr "WebSocket bağlantı hatası"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "Çarşamba"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Haftalık"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Haftalık olarak %{day} günü %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5020,7 +5380,7 @@ msgstr "Çalışan Süreçler"
 msgid "Workers"
 msgstr "Çalışanlar"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Çalışma alanı"
@@ -5107,6 +5467,9 @@ msgstr "Eski kodlarınız artık çalışmayacak."
 msgid "Your passkeys"
 msgstr "Geçiş Anahtarlarınız"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Son Yedekleme Hatası"
+
 #~ msgid "Apply"
 #~ msgstr "Uygula"
 
@@ -5131,9 +5494,6 @@ msgstr "Geçiş Anahtarlarınız"
 #~ msgid "Ok"
 #~ msgstr "Tamam"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Lütfen gerekli alanları doldurun"
-
 #~ msgid "Recover"
 #~ msgstr "İyileşmek"
 

+ 385 - 23
app/src/language/uk_UA/app.po

@@ -112,7 +112,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "2FA Налаштування"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Про программу"
 
@@ -138,6 +138,7 @@ msgstr "ACME Логін"
 msgid "Action"
 msgstr "Дія"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -354,6 +355,11 @@ msgstr "Автоматичний"
 msgid "auto = CPU cores"
 msgstr "Auto = CPU ядра"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Автоматичне резервне копіювання"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Автоматичне оновлення"
@@ -395,7 +401,7 @@ msgstr "На головну"
 msgid "Back to List"
 msgstr "Повернутися до списку"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Резервна копія"
 
@@ -407,10 +413,40 @@ msgstr "Перевірка цілісності резервного файлу
 msgid "Backup file not found: {0}"
 msgstr "Файл резервної копії не знайдено: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "Резервну копію успішно завантажено"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Шлях резервного копіювання"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "Шлях резервного копіювання не існує: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "Шлях резервного копіювання не є директорією: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr ""
+"Шлях резервного копіювання необхідний для резервного копіювання "
+"спеціального каталогу"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Шлях резервного копіювання не входить до наданих шляхів доступу: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Розклад резервного копіювання"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Тип резервної копії"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Хвилини до блокування"
@@ -466,6 +502,14 @@ msgstr "Нижче наведені елементи, які ви обрали 
 msgid "Block is nil"
 msgstr "Блок є nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Обидві конфігурації"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "І конфігурація Nginx, і конфігурація Nginx UI"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Створено за допомогою"
@@ -538,6 +582,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Скасувати"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Не вдається отримати доступ до шляху резервної копії {0}: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Неможливо отримати доступ до шляху зберігання {0}: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Не вдається змінити початковий пароль користувача в демонстраційному режимі"
@@ -977,12 +1029,12 @@ msgstr "Вміст"
 msgid "Copied"
 msgstr "Скопійовано"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Скопійовано!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Копіювати"
 
@@ -1020,7 +1072,7 @@ msgstr "Створити"
 msgid "Create Another"
 msgstr "Створити ще один"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Створити резервну копію"
 
@@ -1032,7 +1084,7 @@ msgstr "Створити файл"
 msgid "Create Folder"
 msgstr "Створити папку"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1041,6 +1093,7 @@ msgstr ""
 "налаштування Nginx UI. Резервні файли будуть автоматично завантажені на ваш "
 "комп’ютер."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1065,6 +1118,10 @@ msgstr "Облікові дані"
 msgid "Credentials"
 msgstr "Повноваження"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Вираз Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "Для поточного облікового запису ввімкнено TOTP."
@@ -1134,17 +1191,42 @@ msgstr "Поточна версія"
 msgid "Custom"
 msgstr "Користувацький"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Спеціальний cron вираз"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Спеціальний каталог"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "Налаштуйте назву локального вузла для відображення в індикаторі середовища."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Щоденно"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Щодня о %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Панель керування"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "День місяця"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "День тижня"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Дні"
@@ -1378,6 +1460,7 @@ msgstr "Не вдалося вимкнути потік %{name} з %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Потік %{name} успішно вимкнено з %{node}"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1513,6 +1596,10 @@ msgstr "Успішно дубльовано локально"
 msgid "Dynamic"
 msgstr "Динамічний"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "напр., 0 0 * * * (щодня о півночі)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Редагувати"
@@ -1639,6 +1726,8 @@ msgstr "Увімкнути TLS"
 msgid "Enable TOTP"
 msgstr "Увімкнути TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1705,6 +1794,18 @@ msgstr "Помилка обробки вмісту"
 msgid "Executable Path"
 msgstr "Шлях до виконуваного файлу"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Виконувати кожен %{day} о %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Виконувати щодня о %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Виконувати щомісяця %{day} числа о %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1731,6 +1832,11 @@ msgstr "Зовнішнє сповіщення"
 msgid "Fail to obtain certificate"
 msgstr "Не вдалося отримати сертифікат"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Не вдалося"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Не вдалося приєднатися до екземпляра виконання: {0}"
@@ -1783,6 +1889,10 @@ msgstr "Не вдалося скопіювати каталог конфігур
 msgid "Failed to create backup"
 msgstr "Не вдалося створити резервну копію"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Не вдалося створити директорію для резервних копій: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Не вдалося створити резервний файл: {0}"
@@ -1807,6 +1917,10 @@ msgstr "Не вдалося створити батьківський катал
 msgid "Failed to create restore directory: {0}"
 msgstr "Не вдалося створити директорію відновлення: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Не вдалося створити каталог сховища {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Не вдалося створити символічне посилання: {0}"
@@ -2019,6 +2133,10 @@ msgstr "Не вдалося запустити тимчасовий контей
 msgid "Failed to verify hashes: {0}"
 msgstr "Не вдалося перевірити хеші: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Не вдалося записати резервний файл: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Не вдалося записати розшифрований файл: {0}"
@@ -2027,6 +2145,10 @@ msgstr "Не вдалося записати розшифрований файл
 msgid "Failed to write encrypted file: {0}"
 msgstr "Не вдалося записати зашифрований файл: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Не вдалося записати файл ключа безпеки: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Не вдалося записати в zip-буфер: {0}"
@@ -2084,6 +2206,14 @@ msgstr "Форматувати код"
 msgid "Format successfully"
 msgstr "Успішно відформатовано"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Формат: хвилина година день місяць день_тижня"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "П'ятниця"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Загальний сертифікат"
@@ -2160,7 +2290,7 @@ msgstr "Вище значення означає краще повторне в
 msgid "History"
 msgstr "Історія"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Головна"
 
@@ -2168,6 +2298,10 @@ msgstr "Головна"
 msgid "Host"
 msgstr "Хост"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Година"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2348,6 +2482,10 @@ msgstr "Недійсне заповнення в розшифрованих да
 msgid "Invalid passcode or recovery code"
 msgstr "Невірний код підтвердження або код відновлення"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Недійсний шлях: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Недійсний код відновлення"
@@ -2417,6 +2555,14 @@ msgstr "Ларк"
 msgid "Lark Custom"
 msgstr "Lark Користувацький"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Статус останнього резервного копіювання"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Час останньої резервної копії"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Остання перевірка"
@@ -2510,10 +2656,17 @@ msgstr "Завантаження даних..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Локальний"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Локальний шлях (напр., /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Розташування"
@@ -2712,6 +2865,10 @@ msgstr "Мінімальний вільний простір"
 msgid "Minimum free space in the cache directory"
 msgstr "Мінімальний вільний простір у кеш-директорії"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Хвилина"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Хвилини"
@@ -2745,11 +2902,24 @@ msgstr "Модуль"
 msgid "Modules"
 msgstr "Модулі"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Понеділок"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Щомісяця"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Щомісяця %{day} числа о %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Багаторядкова директива"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2849,6 +3019,11 @@ msgstr "Конфігурація Nginx не містить sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Конфігурація Nginx не містить stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Конфігурація Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Каталог конфігурації Nginx не встановлено"
@@ -2986,6 +3161,11 @@ msgstr "Теоретична максимальна продуктивність
 msgid "Nginx UI already installed"
 msgstr "Nginx UI вже встановлено"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Конфігурація інтерфейсу Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Конфігурацію Nginx UI відновлено"
@@ -3165,6 +3345,7 @@ msgstr "Офлайн"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3173,7 +3354,6 @@ msgstr "Офлайн"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "Гаразд"
 
@@ -3307,6 +3487,10 @@ msgstr "Паролі не збігаються"
 msgid "Path"
 msgstr "Шлях"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Шлях не входить до наданих шляхів доступу: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Шлях: {0} не знаходиться в каталозі конфігурації nginx: {1}"
@@ -3315,6 +3499,11 @@ msgstr "Шлях: {0} не знаходиться в каталозі конфі
 msgid "Payload resource is nil"
 msgstr "Ресурс навантаження є nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "Очікується"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Виконати"
@@ -3384,6 +3573,10 @@ msgstr "Будь ласка, введіть токен безпеки, отри
 msgid "Please fill all fields correctly"
 msgstr "Будь ласка, заповніть усі поля правильно"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Будь ласка, заповніть обов’язкові поля конфігурації S3"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3445,8 +3638,8 @@ msgstr "Будь ласка, введіть ваш пароль!"
 msgid "Please input your username!"
 msgstr "Будь ласка, введіть ваше ім'я користувача!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Будь ласка, увійдіть."
 
@@ -3460,7 +3653,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Будь ласка, усуньте всі проблеми перед продовженням встановлення"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 "Будь ласка, збережіть цей токен безпеки, він знадобиться вам для "
@@ -3937,6 +4130,109 @@ msgstr "Режим роботи"
 msgid "Running"
 msgstr "Біг"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "Ідентифікатор ключа доступу S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "Ідентифікатор ключа доступу S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "Потрібен ідентифікатор ключа доступу S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3-відро"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Доступ до сховища S3 заборонено: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "Потрібен S3-відро"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "Назва S3-відра"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "Конфігурація S3 неповна: відсутній {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "Тест підключення S3 не вдався"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "Тест підключення S3 не вдався: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "Тест підключення S3 успішний"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "Облікові дані S3 недійсні: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "Кінцева точка S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "Кінцева точка S3 недійсна: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "Потрібна кінцева точка S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "URL-адреса кінцевої точки S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "Шлях S3 (напр., backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "Регіон S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "Регіон S3 (напр., us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "Секретний ключ доступу S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "Секретний ключ доступу S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "Потрібен секретний ключ доступу S3"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "Помилка завантаження в S3: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Субота"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -4016,6 +4312,14 @@ msgstr ""
 "Відскануйте QR-код за допомогою мобільного телефону, щоб додати обліковий "
 "запис до програми."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Розклад"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Тип розкладу"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -4041,7 +4345,7 @@ msgstr "Налаштування безпеки"
 msgid "Security Token"
 msgstr "Токен безпеки"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Інформація про токен безпеки"
 
@@ -4300,6 +4604,29 @@ msgstr "Зупинено"
 msgid "Storage"
 msgstr "Сховище"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Конфігурація сховища"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Шлях зберігання"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "Шлях до сховища обов’язковий"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Шлях зберігання не входить до наданих шляхів доступу: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Тип сховища"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Потік увімкнено"
@@ -4324,10 +4651,17 @@ msgstr "Каталог streams-enabled не існує"
 msgid "Stub Status Port"
 msgstr "Порт статусу Stub"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Успіх"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Неділя"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4443,7 +4777,7 @@ msgstr "Синхронізація"
 msgid "System"
 msgstr "Система"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Резервне копіювання системи"
 
@@ -4460,7 +4794,6 @@ msgid "System Restore"
 msgstr "Відновлення системи"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Систему успішно відновлено."
 
@@ -4484,6 +4817,10 @@ msgstr "Термінал"
 msgid "Terminal Start Command"
 msgstr "Команда запуску терміналу"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Перевірити підключення S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4665,7 +5002,7 @@ msgstr ""
 "Ця операція лише видалить сертифікат із бази даних. Файли сертифіката у "
 "файловій системі не будуть видалені."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4703,6 +5040,10 @@ msgstr "Це оновить або перевстановить Nginx UI на %{
 msgid "Throttle"
 msgstr "Обмеження"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Четвер"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4821,6 +5162,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Кошик"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Вівторок"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Потрібна двофакторна аутентифікація"
@@ -4837,6 +5182,10 @@ msgstr "Тип"
 msgid "Unknown"
 msgstr "Невідомо"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Тип резервної копії не підтримується: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Оновити пароль"
@@ -4849,6 +5198,7 @@ msgstr "Оновити профіль"
 msgid "Update successfully"
 msgstr "Успішно оновлено"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4861,7 +5211,7 @@ msgstr "Успішно оновлено"
 msgid "Updated at"
 msgstr "Оновлено"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4991,10 +5341,10 @@ msgstr "Переглянутий"
 msgid "Waiting processes"
 msgstr "Процеси очікування"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Попередження"
 
@@ -5037,6 +5387,18 @@ msgstr "Налаштування WebAuthn не налаштовані"
 msgid "WebSocket connection error"
 msgstr "Помилка підключення WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "середа"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Щотижня"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Щотижня в %{day} о %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5083,7 +5445,7 @@ msgstr "Робочі процеси"
 msgid "Workers"
 msgstr "Робочі процеси"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Робоча область"
@@ -5168,6 +5530,9 @@ msgstr "Ваші старі коди більше не працюватимут
 msgid "Your passkeys"
 msgstr "Ваші ключі доступу"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Помилка останнього резервного копіювання"
+
 #~ msgid "Apply"
 #~ msgstr "Застосувати"
 
@@ -5192,9 +5557,6 @@ msgstr "Ваші ключі доступу"
 #~ msgid "Ok"
 #~ msgstr "Гаразд"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Будь ласка, заповніть обов’язкові поля"
-
 #~ msgid "Recover"
 #~ msgstr "Відновити"
 

+ 383 - 23
app/src/language/vi_VN/app.po

@@ -103,7 +103,7 @@ msgstr "2fa"
 msgid "2FA Settings"
 msgstr "Cài đặt 2FA"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "Tác giả"
 
@@ -129,6 +129,7 @@ msgstr "Người dùng ACME"
 msgid "Action"
 msgstr "Hành động"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -341,6 +342,11 @@ msgstr "Tự động"
 msgid "auto = CPU cores"
 msgstr "auto = lõi cpu"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "Sao lưu tự động"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "Tự động làm mới"
@@ -382,7 +388,7 @@ msgstr "Trở về trang chủ"
 msgid "Back to List"
 msgstr "Quay lại danh sách"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "Sao lưu"
 
@@ -394,10 +400,38 @@ msgstr "Kiểm tra tính toàn vẹn của tập tin sao lưu thất bại, có
 msgid "Backup file not found: {0}"
 msgstr "Không tìm thấy tệp sao lưu: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "Đã tải xuống bản sao lưu thành công"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "Đường dẫn sao lưu"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "Đường dẫn sao lưu không tồn tại: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "Đường dẫn sao lưu không phải là thư mục: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "Đường dẫn sao lưu là bắt buộc để sao lưu thư mục tùy chỉnh"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "Đường dẫn sao lưu không nằm trong các đường dẫn truy cập được cấp: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "Lịch trình sao lưu"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "Loại sao lưu"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "Phút Ngưỡng Cấm"
@@ -453,6 +487,14 @@ msgstr "Dưới đây là các mục đã chọn mà bạn muốn sửa hàng lo
 msgid "Block is nil"
 msgstr "Khối là nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "Cả hai cấu hình"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Cả cấu hình Nginx và cấu hình giao diện Nginx"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "Xây dựng với"
@@ -524,6 +566,14 @@ msgstr ""
 msgid "Cancel"
 msgstr "Huỷ"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "Không thể truy cập đường dẫn sao lưu {0}: {1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "Không thể truy cập đường dẫn lưu trữ {0}: {1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "Không thể thay đổi mật khẩu người dùng ban đầu trong chế độ demo"
@@ -962,12 +1012,12 @@ msgstr "Nội dung"
 msgid "Copied"
 msgstr "Đã sao chép"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "Đã sao chép!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "Sao chép"
 
@@ -1003,7 +1053,7 @@ msgstr "Tạo"
 msgid "Create Another"
 msgstr "Tạo thêm"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "Tạo bản sao lưu"
 
@@ -1015,7 +1065,7 @@ msgstr "Tạo tệp"
 msgid "Create Folder"
 msgstr "Tạo thư mục"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
@@ -1023,6 +1073,7 @@ msgstr ""
 "Tạo bản sao lưu hệ thống bao gồm cấu hình Nginx và cài đặt Nginx UI. Các "
 "tệp sao lưu sẽ tự động được tải xuống máy tính của bạn."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1047,6 +1098,10 @@ msgstr "Chứng chỉ"
 msgid "Credentials"
 msgstr "Chứng chỉ"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Biểu thức Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "Tài khoản hiện tại đã bật TOTP."
@@ -1080,17 +1135,42 @@ msgstr "Phiên bản hiện tại"
 msgid "Custom"
 msgstr "Tuỳ chỉnh"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "Biểu thức cron tùy chỉnh"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "Thư mục tùy chỉnh"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "Tùy chỉnh tên nút cục bộ để hiển thị trong chỉ báo môi trường."
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "Hàng ngày"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "Hàng ngày lúc %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "Ngày trong tháng"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "Ngày trong tuần"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "Ngày"
@@ -1288,6 +1368,7 @@ msgstr "Không thể tắt luồng %{name} từ %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Đã vô hiệu hóa luồng %{name} từ %{node} thành công"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1424,6 +1505,10 @@ msgstr "Nhân bản thành công vào bộ nhớ cục bộ"
 msgid "Dynamic"
 msgstr "Động"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "vd, 0 0 * * * (hàng ngày vào nửa đêm)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "Chỉnh sửa"
@@ -1550,6 +1635,8 @@ msgstr "Bật TLS"
 msgid "Enable TOTP"
 msgstr "Bật TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1616,6 +1703,18 @@ msgstr "Lỗi xử lý nội dung"
 msgid "Executable Path"
 msgstr "Đường dẫn thực thi"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "Thực thi vào mỗi %{day} lúc %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "Thực hiện mỗi ngày vào lúc %{time}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "Thực thi vào mỗi tháng vào ngày %{day} lúc %{time}"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1642,6 +1741,11 @@ msgstr "Thông báo bên ngoài"
 msgid "Fail to obtain certificate"
 msgstr "Không thể lấy chứng chỉ"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Thất bại"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Không thể gắn kết với phiên bản thực thi: {0}"
@@ -1694,6 +1798,10 @@ msgstr "Không thể sao chép thư mục cấu hình Nginx: {0}"
 msgid "Failed to create backup"
 msgstr "Không thể tạo bản sao lưu"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Không thể tạo thư mục sao lưu: {0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "Không thể tạo tệp sao lưu: {0}"
@@ -1718,6 +1826,10 @@ msgstr "Không thể tạo thư mục cha: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Không thể tạo thư mục khôi phục: {0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "Không thể tạo thư mục lưu trữ {0}: {1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "Không thể tạo liên kết tượng trưng: {0}"
@@ -1930,6 +2042,10 @@ msgstr "Không thể khởi động container tạm thời: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Không thể xác minh băm: {0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Không thể ghi tệp sao lưu: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "Không thể ghi tệp đã giải mã: {0}"
@@ -1938,6 +2054,10 @@ msgstr "Không thể ghi tệp đã giải mã: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Không thể ghi tệp đã mã hóa: {0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "Không thể ghi tệp khóa bảo mật: {0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Không thể ghi vào bộ đệm zip: {0}"
@@ -1995,6 +2115,14 @@ msgstr "Định dạng code"
 msgid "Format successfully"
 msgstr "Định dạng thành công"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "Định dạng: phút giờ ngày tháng thứ_trong_tuần"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "Thứ Sáu"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "Chứng chỉ chung"
@@ -2071,7 +2199,7 @@ msgstr "Giá trị cao hơn có nghĩa là tái sử dụng kết nối tốt h
 msgid "History"
 msgstr "Lịch sử"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "Trang chủ"
 
@@ -2079,6 +2207,10 @@ msgstr "Trang chủ"
 msgid "Host"
 msgstr "Máy chủ"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Giờ"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2258,6 +2390,10 @@ msgstr "Đệm không hợp lệ trong dữ liệu đã giải mã"
 msgid "Invalid passcode or recovery code"
 msgstr "Mã xác thực hoặc mã khôi phục không hợp lệ"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Đường dẫn không hợp lệ: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "Mã khôi phục không hợp lệ"
@@ -2327,6 +2463,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark Tùy chỉnh"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "Trạng thái sao lưu cuối cùng"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "Thời gian sao lưu cuối cùng"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "Kiểm tra lần cuối lúc"
@@ -2420,10 +2564,17 @@ msgstr "Đang tải dữ liệu..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "Cục bộ"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "Đường dẫn cục bộ (vd: /var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Location"
@@ -2622,6 +2773,10 @@ msgstr "Dung lượng trống tối thiểu"
 msgid "Minimum free space in the cache directory"
 msgstr "Dung lượng trống tối thiểu trong thư mục bộ nhớ đệm"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Phút"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "Phút"
@@ -2655,11 +2810,24 @@ msgstr "Mô-đun"
 msgid "Modules"
 msgstr "Mô-đun"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "Thứ Hai"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "Hàng tháng"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "Hàng tháng vào ngày %{day} lúc %{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "Chỉ thị nhiều dòng"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2759,6 +2927,11 @@ msgstr "Cấu hình Nginx không bao gồm sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Cấu hình Nginx không bao gồm stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Cấu hình Nginx"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Thư mục cấu hình Nginx chưa được thiết lập"
@@ -2896,6 +3069,11 @@ msgstr "Hiệu suất tối đa lý thuyết của Nginx"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI đã được cài đặt"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Cấu hình giao diện Nginx"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Cấu hình Nginx UI đã được khôi phục"
@@ -3073,6 +3251,7 @@ msgstr "Ngoại tuyến"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3081,7 +3260,6 @@ msgstr "Ngoại tuyến"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "Đồng ý"
 
@@ -3215,6 +3393,10 @@ msgstr "Mật khẩu không khớp"
 msgid "Path"
 msgstr "Đường dẫn"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "Đường dẫn không nằm trong các đường dẫn truy cập được cấp: {0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Đường dẫn: {0} không nằm trong thư mục cấu hình nginx: {1}"
@@ -3223,6 +3405,11 @@ msgstr "Đường dẫn: {0} không nằm trong thư mục cấu hình nginx: {1
 msgid "Payload resource is nil"
 msgstr "Tài nguyên tải trọng là nil"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "Đang chờ"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "Thực hiện"
@@ -3292,6 +3479,10 @@ msgstr "Vui lòng nhập mã bảo mật nhận được trong quá trình sao l
 msgid "Please fill all fields correctly"
 msgstr "Vui lòng điền đầy đủ và chính xác tất cả các trường"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "Vui lòng điền vào các trường cấu hình S3 bắt buộc"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3348,8 +3539,8 @@ msgstr "Vui lòng nhập mật khẩu!"
 msgid "Please input your username!"
 msgstr "Vui lòng nhập username!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "Vui lòng đăng nhập."
 
@@ -3361,7 +3552,7 @@ msgstr "Lưu ý đơn vị cấu hình thời gian bên dưới được tính b
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Vui lòng giải quyết tất cả các vấn đề trước khi tiến hành cài đặt"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "Vui lòng lưu token bảo mật này, bạn sẽ cần nó để khôi phục:"
 
@@ -3831,6 +4022,109 @@ msgstr "Chế độ chạy"
 msgid "Running"
 msgstr "Running"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "ID khóa truy cập S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "ID khóa truy cập S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "Yêu cầu ID khóa truy cập S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "Thùng S3"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "Truy cập vào bộ chứa S3 bị từ chối: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "Yêu cầu phải có S3 bucket"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "Tên bucket S3"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "Cấu hình S3 không đầy đủ: thiếu {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "Kiểm tra kết nối S3 thất bại"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "Kiểm tra kết nối S3 thất bại: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "Kiểm tra kết nối S3 thành công"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "Thông tin xác thực S3 không hợp lệ: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "Điểm cuối S3"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "Điểm cuối S3 không hợp lệ: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "Yêu cầu điểm cuối S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "URL điểm cuối S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "Đường dẫn S3 (vd: backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "Vùng S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "Vùng S3 (vd: us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "Khóa truy cập bí mật S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "Khóa truy cập bí mật S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "Yêu cầu khóa truy cập bí mật S3"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "Tải lên S3 thất bại: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "Thứ Bảy"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3908,6 +4202,14 @@ msgstr "Đường dẫn sbin không tồn tại"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "Quét mã QR bằng điện thoại di động của bạn để thêm tài khoản vào ứng dụng."
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "Lịch trình"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "Loại lịch trình"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3933,7 +4235,7 @@ msgstr "Cài đặt bảo mật"
 msgid "Security Token"
 msgstr "Mã bảo mật"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "Thông tin mã bảo mật"
 
@@ -4190,6 +4492,29 @@ msgstr "Đã dừng"
 msgid "Storage"
 msgstr "Storage"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "Cấu hình lưu trữ"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "Đường dẫn lưu trữ"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "Đường dẫn lưu trữ là bắt buộc"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "Đường dẫn lưu trữ không nằm trong các đường dẫn truy cập được cấp: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "Loại lưu trữ"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Luồng đã được bật"
@@ -4214,10 +4539,17 @@ msgstr "Thư mục streams-enabled không tồn tại"
 msgid "Stub Status Port"
 msgstr "Cổng trạng thái stub"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "Thành công"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "Chủ nhật"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4333,7 +4665,7 @@ msgstr "Đồng bộ hóa"
 msgid "System"
 msgstr "Thông tin"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "Sao lưu hệ thống"
 
@@ -4350,7 +4682,6 @@ msgid "System Restore"
 msgstr "Khôi phục hệ thống"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "Hệ thống đã được khôi phục thành công."
 
@@ -4374,6 +4705,10 @@ msgstr "Thiết bị đầu cuối"
 msgid "Terminal Start Command"
 msgstr "Lệnh Khởi động Terminal"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Kiểm tra kết nối S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4553,7 +4888,7 @@ msgstr ""
 "Thao tác này sẽ chỉ xóa chứng chỉ khỏi cơ sở dữ liệu. Các tệp chứng chỉ "
 "trên hệ thống tệp sẽ không bị xóa."
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4593,6 +4928,10 @@ msgstr ""
 msgid "Throttle"
 msgstr "Hạn chế"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Thứ Năm"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4711,6 +5050,10 @@ msgstr ""
 msgid "Trash"
 msgstr "Thùng rác"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Thứ Ba"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "Yêu cầu xác thực hai yếu tố"
@@ -4727,6 +5070,10 @@ msgstr "Loại"
 msgid "Unknown"
 msgstr "Không xác định"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Loại sao lưu không được hỗ trợ: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "Cập nhật mật khẩu"
@@ -4739,6 +5086,7 @@ msgstr "Cập nhật hồ sơ"
 msgid "Update successfully"
 msgstr "Cập nhật thành công"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4751,7 +5099,7 @@ msgstr "Cập nhật thành công"
 msgid "Updated at"
 msgstr "Ngày cập nhật"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4881,10 +5229,10 @@ msgstr "Đã xem"
 msgid "Waiting processes"
 msgstr "Quá trình chờ đợi"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "Lưu ý"
 
@@ -4927,6 +5275,18 @@ msgstr "Cài đặt WebAuthn chưa được cấu hình"
 msgid "WebSocket connection error"
 msgstr "Lỗi kết nối WebSocket"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "Thứ Tư"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "Hàng tuần"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "Hàng tuần vào %{day} lúc %{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4971,7 +5331,7 @@ msgstr "Tiến trình công nhân"
 msgid "Workers"
 msgstr "Công nhân"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "Không gian làm việc"
@@ -5056,6 +5416,9 @@ msgstr "Mã cũ của bạn sẽ không còn hoạt động nữa."
 msgid "Your passkeys"
 msgstr "Khóa truy cập của bạn"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Lỗi sao lưu cuối cùng"
+
 #~ msgid "Apply"
 #~ msgstr "Áp dụng"
 
@@ -5080,9 +5443,6 @@ msgstr "Khóa truy cập của bạn"
 #~ msgid "Ok"
 #~ msgstr "Đồng ý"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Vui lòng điền vào các trường bắt buộc"
-
 #~ msgid "Recover"
 #~ msgstr "Khôi phục"
 

+ 383 - 26
app/src/language/zh_CN/app.po

@@ -107,7 +107,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgstr "2FA 设置"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "关于"
 
@@ -133,6 +133,7 @@ msgstr "ACME 用户"
 msgid "Action"
 msgstr "操作"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -345,6 +346,11 @@ msgstr "自动"
 msgid "auto = CPU cores"
 msgstr "自动 = CPU 线程数"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "自动备份"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "自动刷新"
@@ -386,7 +392,7 @@ msgstr "返回首页"
 msgid "Back to List"
 msgstr "返回列表"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "备份"
 
@@ -398,10 +404,38 @@ msgstr "备份文件完整性检查失败,可能已被篡改"
 msgid "Backup file not found: {0}"
 msgstr "未找到备份文件: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "已成功下载备份"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "备份路径"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "备份路径不存在: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "备份路径不是目录: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "自定义目录备份需要备份路径"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "备份路径不在授予的访问路径中: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "备份计划"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "备份类型"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "禁止阈值(分钟)"
@@ -457,6 +491,14 @@ msgstr "以下是您选定的需要批量修改的项目"
 msgid "Block is nil"
 msgstr "区块为空"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "两者配置"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Nginx 和 Nginx UI 配置"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "构建基于"
@@ -526,6 +568,14 @@ msgstr "基于 worker_processes * worker_connections 计算得出。实际性能
 msgid "Cancel"
 msgstr "取消"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "无法访问备份路径 {0}:{1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "无法访问存储路径 {0}:{1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "不可在 Demo 中修改初始用户的密码"
@@ -946,12 +996,12 @@ msgstr "内容"
 msgid "Copied"
 msgstr "已拷贝"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "已拷贝!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "拷贝"
 
@@ -987,7 +1037,7 @@ msgstr "创建"
 msgid "Create Another"
 msgstr "再创建一个"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "创建备份"
 
@@ -999,12 +1049,13 @@ msgstr "创建文件"
 msgid "Create Folder"
 msgstr "创建文件夹"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 msgstr "创建系统备份,包括 Nginx 配置和 Nginx UI 设置。备份文件将自动下载到你的电脑。"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1029,6 +1080,10 @@ msgstr "DNS 凭证"
 msgid "Credentials"
 msgstr "凭证"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron 表达式"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "当前账户已启用 TOTP 验证。"
@@ -1062,17 +1117,42 @@ msgstr "当前版本"
 msgid "Custom"
 msgstr "自定义"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "自定义 cron 表达式"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "自定义目录"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "自定义显示在环境指示器中的本地服务器名称。"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "每日"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "每天 %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "仪表盘"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "月中的某天"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "星期几"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "天"
@@ -1270,6 +1350,7 @@ msgstr "在 %{node} 中启用 %{name} 失败"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "在 %{node} 上禁用 %{name} 成功"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1402,6 +1483,10 @@ msgstr "成功复制到本地"
 msgid "Dynamic"
 msgstr "动态"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "例如:0 0 * * *(每天午夜)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "编辑"
@@ -1528,6 +1613,8 @@ msgstr "启用 TLS"
 msgid "Enable TOTP"
 msgstr "启用 TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1594,6 +1681,18 @@ msgstr "内容处理错误"
 msgid "Executable Path"
 msgstr "可执行文件路径"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "每周%{day}的%{time}执行"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "每天 %{time} 执行"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "每月 %{day} 日 %{time} 执行"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1620,6 +1719,11 @@ msgstr "外部通知"
 msgid "Fail to obtain certificate"
 msgstr "获取证书失败"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "失败"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "连接执行实例失败:{0}"
@@ -1672,6 +1776,10 @@ msgstr "复制 Nginx 配置目录失败:{0}"
 msgid "Failed to create backup"
 msgstr "创建备份失败"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "创建备份目录失败:{0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "创建备份文件失败:{0}"
@@ -1696,6 +1804,10 @@ msgstr "创建父目录失败:{0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "创建还原目录失败:{0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "创建存储目录失败 {0}:{1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "创建符号链接失败:{0}"
@@ -1908,6 +2020,10 @@ msgstr "启动临时容器失败:{0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "验证哈希值失败:{0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "写入备份文件失败: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "写入解密文件失败:{0}"
@@ -1916,6 +2032,10 @@ msgstr "写入解密文件失败:{0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "写入加密文件失败:{0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "无法写入安全密钥文件:{0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "写入 zip 缓冲区失败:{0}"
@@ -1971,6 +2091,14 @@ msgstr "代码格式化"
 msgid "Format successfully"
 msgstr "格式化成功"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "格式:分钟 小时 日 月 星期"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "星期五"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "普通证书"
@@ -2047,7 +2175,7 @@ msgstr "更高的值意味着更好的连接再利用"
 msgid "History"
 msgstr "历史"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "首页"
 
@@ -2055,6 +2183,10 @@ msgstr "首页"
 msgid "Host"
 msgstr "主机"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "小时"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2226,6 +2358,10 @@ msgstr "解密数据中的无效填充"
 msgid "Invalid passcode or recovery code"
 msgstr "二次验证码或恢复代码无效"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "无效路径: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "无效的恢复代码"
@@ -2293,6 +2429,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark 自定义"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "上次备份状态"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "上次备份时间"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "最后检查时间"
@@ -2386,10 +2530,17 @@ msgstr "正在加载数据..."
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "本地"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "本地路径(例如:/var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Location"
@@ -2581,6 +2732,10 @@ msgstr "最小可用空间"
 msgid "Minimum free space in the cache directory"
 msgstr "缓存目录中的最小可用空间"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "分钟"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "分钟"
@@ -2614,11 +2769,24 @@ msgstr "模块"
 msgid "Modules"
 msgstr "模块"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "星期一"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "每月"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "每月%{day}日%{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "多行指令"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2718,6 +2886,11 @@ msgstr "Nginx Conf 中未引用 sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx Conf 中未引用 stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Nginx配置"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "未设置 Nginx 配置目录"
@@ -2855,6 +3028,11 @@ msgstr "Nginx 理论最高性能"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI 已安装"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Nginx 界面配置"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Nginx 用户界面配置已恢复"
@@ -3026,6 +3204,7 @@ msgstr "离线"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3034,7 +3213,6 @@ msgstr "离线"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "确定"
 
@@ -3164,6 +3342,10 @@ msgstr "密码不匹配"
 msgid "Path"
 msgstr "路径"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "路径不在授予的访问路径中:{0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "路径:{0} 不在 nginx 配置目录下:{1}"
@@ -3172,6 +3354,11 @@ msgstr "路径:{0} 不在 nginx 配置目录下:{1}"
 msgid "Payload resource is nil"
 msgstr "有效载荷资源为空"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "待处理"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "执行"
@@ -3237,6 +3424,10 @@ msgstr "请输入备份时收到的安全令牌"
 msgid "Please fill all fields correctly"
 msgstr "请正确填写所有字段"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "请填写必填的 S3 配置字段"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3289,8 +3480,8 @@ msgstr "请输入您的密码!"
 msgid "Please input your username!"
 msgstr "请输入您的用户名!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "请登录。"
 
@@ -3302,7 +3493,7 @@ msgstr "请注意,下面的时间单位配置均以秒为单位。"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "请在安装前解决所有问题"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "请保存此安全令牌,恢复时会用到它:"
 
@@ -3763,6 +3954,109 @@ msgstr "运行模式"
 msgid "Running"
 msgstr "运行中"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "S3访问密钥ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "S3 访问密钥 ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "必须提供 S3 访问密钥 ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3 存储桶"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "S3存储桶访问被拒绝:{0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "必须填写 S3 存储桶"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "S3 存储桶名称"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "S3 配置不完整:缺少 {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "S3连接测试失败"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "S3连接测试失败: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "S3连接测试成功"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "S3 凭证无效: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "S3 终端节点"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "S3终端节点无效:{0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "必须提供 S3 端点"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "S3 终端节点 URL"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "S3 路径(例如 backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "S3 区域"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "S3 区域(例如 us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "S3 秘密访问密钥"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "S3 秘密访问密钥"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "必须提供S3秘密访问密钥"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "S3上传失败:{0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "星期六"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3840,6 +4134,14 @@ msgstr "Sbin 路径不存在"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "用手机扫描二维码,将账户添加到应用程序中。"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "计划"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "计划类型"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3865,7 +4167,7 @@ msgstr "安全设置"
 msgid "Security Token"
 msgstr "安全 Token"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "安全令牌信息"
 
@@ -4120,6 +4422,29 @@ msgstr "已停止"
 msgid "Storage"
 msgstr "存储"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "存储配置"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "存储路径"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "存储路径是必填项"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "存储路径不在授予的访问路径中: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "存储类型"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "Stream 已启用"
@@ -4144,10 +4469,17 @@ msgstr "Streams-enabled 目录不存在"
 msgid "Stub Status Port"
 msgstr "Stub 状态端口"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "成功"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "星期日"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4259,7 +4591,7 @@ msgstr "同步"
 msgid "System"
 msgstr "系统"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "系统备份"
 
@@ -4276,7 +4608,6 @@ msgid "System Restore"
 msgstr "系统还原"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "系统恢复成功。"
 
@@ -4300,6 +4631,10 @@ msgstr "终端"
 msgid "Terminal Start Command"
 msgstr "终端启动命令"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "测试 S3 连接"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4452,7 +4787,7 @@ msgid ""
 "certificate files on the file system will not be deleted."
 msgstr "此操作只会从数据库中删除证书。文件系统中的证书文件不会被删除。"
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4484,6 +4819,10 @@ msgstr "将 %{nodeNames} 上的 Nginx UI 升级或重新安装到 %{version} 版
 msgid "Throttle"
 msgstr "限流"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "星期四"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4590,6 +4929,10 @@ msgstr "TOTP 是一种使用基于时间的一次性密码算法的双因素身
 msgid "Trash"
 msgstr "回收站"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "星期二"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "需要两步验证"
@@ -4606,6 +4949,10 @@ msgstr "类型"
 msgid "Unknown"
 msgstr "未知"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "不支持的备份类型: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "更新密码"
@@ -4618,6 +4965,7 @@ msgstr "更新资料"
 msgid "Update successfully"
 msgstr "更新成功"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4630,7 +4978,7 @@ msgstr "更新成功"
 msgid "Updated at"
 msgstr "修改时间"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4760,10 +5108,10 @@ msgstr "已查看"
 msgid "Waiting processes"
 msgstr "等待处理"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "警告"
 
@@ -4799,6 +5147,18 @@ msgstr "WebAuthn 未配置"
 msgid "WebSocket connection error"
 msgstr "WebSocket 连接错误"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "星期三"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "每周"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "每周%{day}的%{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4838,7 +5198,7 @@ msgstr "工作进程"
 msgid "Workers"
 msgstr "Workers"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "工作区"
@@ -4916,6 +5276,9 @@ msgstr "您的旧代码将不再有效。"
 msgid "Your passkeys"
 msgstr "你的 Passkeys"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "最后一次备份错误"
+
 #~ msgid "Apply"
 #~ msgstr "应用"
 
@@ -4940,9 +5303,6 @@ msgstr "你的 Passkeys"
 #~ msgid "Ok"
 #~ msgstr "确定"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "请填写必填字段"
-
 #~ msgid "Recover"
 #~ msgstr "恢复"
 
@@ -5017,9 +5377,6 @@ msgstr "你的 Passkeys"
 #~ msgid "Nginx Conf Include Streams Enabled"
 #~ msgstr "Nginx Conf 中引用启用 Streams 目录"
 
-#~ msgid "Sites Directory"
-#~ msgstr "网站目录"
-
 #~ msgid "Format error %{msg}"
 #~ msgstr "保存错误 %{msg}"
 

+ 383 - 26
app/src/language/zh_TW/app.po

@@ -111,7 +111,7 @@ msgstr "雙因素驗證"
 msgid "2FA Settings"
 msgstr "多重要素驗證設定"
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgstr "關於"
 
@@ -137,6 +137,7 @@ msgstr "ACME 使用者"
 msgid "Action"
 msgstr "操作"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
@@ -349,6 +350,11 @@ msgstr "自動"
 msgid "auto = CPU cores"
 msgstr "auto = CPU 核心數"
 
+#: src/routes/modules/backup.ts:27
+#: src/views/backup/AutoBackup/AutoBackup.vue:250
+msgid "Auto Backup"
+msgstr "自動備份"
+
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgstr "自動重新整理"
@@ -390,7 +396,7 @@ msgstr "返回首頁"
 msgid "Back to List"
 msgstr "返回列表"
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgstr "備份"
 
@@ -402,10 +408,38 @@ msgstr "備份檔案完整性檢查失敗,可能已被篡改"
 msgid "Backup file not found: {0}"
 msgstr "找不到備份檔案: {0}"
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgstr "備份已成功下載"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:54
+msgid "Backup Path"
+msgstr "備份路徑"
+
+#: src/constants/errors/backup.ts:75
+msgid "Backup path does not exist: {0}"
+msgstr "備份路徑不存在: {0}"
+
+#: src/constants/errors/backup.ts:77
+msgid "Backup path is not a directory: {0}"
+msgstr "備份路徑不是目錄: {0}"
+
+#: src/constants/errors/backup.ts:62
+msgid "Backup path is required for custom directory backup"
+msgstr "自訂目錄備份需要備份路徑"
+
+#: src/constants/errors/backup.ts:60
+msgid "Backup path not in granted access paths: {0}"
+msgstr "備份路徑不在授予的訪問路徑中: {0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:141
+msgid "Backup Schedule"
+msgstr "備份計劃"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:24
+msgid "Backup Type"
+msgstr "備份類型"
+
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgstr "封禁閾值分鐘數"
@@ -461,6 +495,14 @@ msgstr "以下是您要批次修改的選定項目"
 msgid "Block is nil"
 msgstr "區塊為空"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:30
+msgid "Both Config"
+msgstr "兩者配置"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:44
+msgid "Both Nginx and Nginx UI Config"
+msgstr "Nginx 和 Nginx UI 配置"
+
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgstr "建構基於"
@@ -530,6 +572,14 @@ msgstr "基於 worker_processes * worker_connections 計算得出。實際效能
 msgid "Cancel"
 msgstr "取消"
 
+#: src/constants/errors/backup.ts:76
+msgid "Cannot access backup path {0}: {1}"
+msgstr "無法存取備份路徑 {0}:{1}"
+
+#: src/constants/errors/backup.ts:79
+msgid "Cannot access storage path {0}: {1}"
+msgstr "無法存取儲存路徑 {0}:{1}"
+
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgstr "無法在示範模式下更改初始使用者密碼"
@@ -950,12 +1000,12 @@ msgstr "內容"
 msgid "Copied"
 msgstr "已複製"
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgstr "已複製!"
 
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgstr "複製"
 
@@ -991,7 +1041,7 @@ msgstr "建立"
 msgid "Create Another"
 msgstr "再建立一個"
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgstr "建立備份"
 
@@ -1003,12 +1053,13 @@ msgstr "建立檔案"
 msgid "Create Folder"
 msgstr "建立資料夾"
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 msgstr "建立系統備份,包括 Nginx 設定與 Nginx UI 設定。備份檔案將自動下載至您的電腦。"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1033,6 +1084,10 @@ msgstr "認證"
 msgid "Credentials"
 msgstr "認證資訊"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron 表達式"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgstr "目前帳戶已啟用 TOTP。"
@@ -1066,17 +1121,42 @@ msgstr "目前版本"
 msgid "Custom"
 msgstr "自訂"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:131
+msgid "Custom cron expression"
+msgstr "自訂 cron 表達式"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:31
+#: src/views/backup/AutoBackup/AutoBackup.vue:45
+msgid "Custom Directory"
+msgstr "自訂目錄"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 msgstr "自訂顯示在環境指示器中的本機節點名稱。"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:19
+msgid "Daily"
+msgstr "每日"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:129
+msgid "Daily at %{time}"
+msgstr "每天 %{time}"
+
 #: src/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgstr "儀錶板"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:184
+msgid "Day of Month"
+msgstr "月中的某天"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:180
+msgid "Day of Week"
+msgstr "星期幾"
+
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgstr "天"
@@ -1274,6 +1354,7 @@ msgstr "停用來自 %{node} 的串流 %{name} 失敗"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "已成功從 %{node} 停用串流 %{name}"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1406,6 +1487,10 @@ msgstr "成功複製至本機"
 msgid "Dynamic"
 msgstr "動態"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr "例如:0 0 * * *(每天午夜)"
+
 #: src/language/curd.ts:8
 msgid "Edit"
 msgstr "編輯"
@@ -1532,6 +1617,8 @@ msgstr "啟用 TLS"
 msgid "Enable TOTP"
 msgstr "啟用 TOTP"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:158
+#: src/views/backup/AutoBackup/AutoBackup.vue:172
 #: src/views/environments/list/envColumns.tsx:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1598,6 +1685,18 @@ msgstr "內容處理錯誤"
 msgid "Executable Path"
 msgstr "可執行檔路徑"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:127
+msgid "Execute on every %{day} at %{time}"
+msgstr "每週%{day}的%{time}執行"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:125
+msgid "Execute on every day at %{time}"
+msgstr "每天 %{time} 執行"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:129
+msgid "Execute on every month on day %{day} at %{time}"
+msgstr "每月 %{day} 日 %{time} 執行"
+
 #: src/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
@@ -1624,6 +1723,11 @@ msgstr "外部通知"
 msgid "Fail to obtain certificate"
 msgstr "取得憑證失敗"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "失敗"
+
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgstr "無法附加到執行實例: {0}"
@@ -1676,6 +1780,10 @@ msgstr "複製 Nginx 設定目錄失敗:{0}"
 msgid "Failed to create backup"
 msgstr "建立備份失敗"
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "建立備份目錄失敗:{0}"
+
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgstr "無法建立備份檔案:{0}"
@@ -1700,6 +1808,10 @@ msgstr "無法建立父目錄:{0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "無法建立還原目錄:{0}"
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr "建立儲存目錄失敗 {0}:{1}"
+
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgstr "建立符號連結失敗:{0}"
@@ -1912,6 +2024,10 @@ msgstr "啟動臨時容器失敗: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "無法驗證雜湊值:{0}"
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "寫入備份檔案失敗: {0}"
+
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgstr "寫入解密檔案失敗:{0}"
@@ -1920,6 +2036,10 @@ msgstr "寫入解密檔案失敗:{0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "寫入加密檔案失敗:{0}"
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr "無法寫入安全金鑰檔案:{0}"
+
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgstr "寫入壓縮緩衝區失敗:{0}"
@@ -1975,6 +2095,14 @@ msgstr "格式化代碼"
 msgid "Format successfully"
 msgstr "成功格式化"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:200
+msgid "Format: minute hour day month weekday"
+msgstr "格式:分鐘 小時 日 月 星期"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:31
+msgid "Friday"
+msgstr "星期五"
+
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgstr "普通憑證"
@@ -2051,7 +2179,7 @@ msgstr "數值越高表示連線重複使用率越好"
 msgid "History"
 msgstr "歷史"
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgstr "首頁"
 
@@ -2059,6 +2187,10 @@ msgstr "首頁"
 msgid "Host"
 msgstr "主機"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "小時"
+
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgstr "HTTP"
@@ -2230,6 +2362,10 @@ msgstr "解密資料中的填充無效"
 msgid "Invalid passcode or recovery code"
 msgstr "無效的密碼或恢復碼"
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "無效路徑: {0}"
+
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgstr "無效的恢復碼"
@@ -2297,6 +2433,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgstr "Lark 自訂"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:188
+msgid "Last Backup Status"
+msgstr "上次備份狀態"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:181
+msgid "Last Backup Time"
+msgstr "上次備份時間"
+
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgstr "上次檢查時間"
@@ -2390,10 +2534,17 @@ msgstr "資料載入中…"
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
+#: src/views/backup/AutoBackup/AutoBackup.vue:78
+#: src/views/backup/AutoBackup/AutoBackup.vue:87
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:46
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgstr "本機"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr "本地路徑(例如:/var/backups)"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgstr "Location"
@@ -2585,6 +2736,10 @@ msgstr "最小剩餘空間"
 msgid "Minimum free space in the cache directory"
 msgstr "快取目錄最小剩餘空間"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "分鐘"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgstr "分鐘"
@@ -2618,11 +2773,24 @@ msgstr "模組"
 msgid "Modules"
 msgstr "模組"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:27
+msgid "Monday"
+msgstr "星期一"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:21
+msgid "Monthly"
+msgstr "每月"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:139
+msgid "Monthly on day %{day} at %{time}"
+msgstr "每月%{day}日%{time}"
+
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgstr "多行指令"
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2722,6 +2890,11 @@ msgstr "Nginx 設定檔未包含 sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx 設定檔未包含 stream-enabled"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:28
+#: src/views/backup/AutoBackup/AutoBackup.vue:42
+msgid "Nginx Config"
+msgstr "Nginx配置"
+
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgstr "Nginx 設定目錄未設定"
@@ -2859,6 +3032,11 @@ msgstr "Nginx 理論最大效能"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI 已安裝"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:29
+#: src/views/backup/AutoBackup/AutoBackup.vue:43
+msgid "Nginx UI Config"
+msgstr "Nginx 介面配置"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgstr "Nginx UI 設定已恢復"
@@ -3030,6 +3208,7 @@ msgstr "離線"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
@@ -3038,7 +3217,6 @@ msgstr "離線"
 #: src/views/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgstr "確定"
 
@@ -3170,6 +3348,10 @@ msgstr "密碼不匹配"
 msgid "Path"
 msgstr "路徑"
 
+#: src/constants/errors/backup.ts:74
+msgid "Path not in granted access paths: {0}"
+msgstr "路徑不在授予的訪問路徑中:{0}"
+
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "路徑:{0} 不在 Nginx 設定目錄:{1} 下"
@@ -3178,6 +3360,11 @@ msgstr "路徑:{0} 不在 Nginx 設定目錄:{1} 下"
 msgid "Payload resource is nil"
 msgstr "有效載荷資源為空"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:192
+#: src/views/backup/AutoBackup/AutoBackup.vue:217
+msgid "Pending"
+msgstr "待處理"
+
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgstr "執行"
@@ -3243,6 +3430,10 @@ msgstr "請輸入備份時收到的安全令牌"
 msgid "Please fill all fields correctly"
 msgstr "請正確填寫所有欄位"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr "請填寫必填的 S3 配置欄位"
+
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3295,8 +3486,8 @@ msgstr "請輸入您的密碼!"
 msgid "Please input your username!"
 msgstr "請輸入您的使用者名稱!"
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgstr "請登入。"
 
@@ -3308,7 +3499,7 @@ msgstr "請注意,以下時間設定單位均為秒。"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "請在繼續安裝前解決所有問題"
 
-#: src/views/system/Backup/BackupCreator.vue:107
+#: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "請儲存此安全令牌,您將需要它進行恢復:"
 
@@ -3769,6 +3960,109 @@ msgstr "執行模式"
 msgid "Running"
 msgstr "執行中"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:79
+#: src/views/backup/AutoBackup/AutoBackup.vue:88
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:47
+msgid "S3"
+msgstr "S3"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:81
+msgid "S3 access key ID"
+msgstr "S3存取金鑰ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:75
+msgid "S3 Access Key ID"
+msgstr "S3 存取金鑰 ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:77
+msgid "S3 access key ID is required"
+msgstr "必須提供 S3 存取金鑰 ID"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:97
+msgid "S3 Bucket"
+msgstr "S3 儲存桶"
+
+#: src/constants/errors/backup.ts:70
+msgid "S3 bucket access denied: {0}"
+msgstr "S3儲存桶存取被拒絕:{0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:99
+msgid "S3 bucket is required"
+msgstr "必須填寫 S3 儲存桶"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:103
+msgid "S3 bucket name"
+msgstr "S3 儲存貯體名稱"
+
+#: src/constants/errors/backup.ts:63
+msgid "S3 configuration is incomplete: missing {0}"
+msgstr "S3 配置不完整:缺少 {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:32
+msgid "S3 connection test failed"
+msgstr "S3連接測試失敗"
+
+#: src/constants/errors/backup.ts:69
+msgid "S3 connection test failed: {0}"
+msgstr "S3連接測試失敗: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:28
+msgid "S3 connection test successful"
+msgstr "S3連接測試成功"
+
+#: src/constants/errors/backup.ts:71
+msgid "S3 credentials are invalid: {0}"
+msgstr "S3 憑證無效: {0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:64
+msgid "S3 Endpoint"
+msgstr "S3 終端節點"
+
+#: src/constants/errors/backup.ts:72
+msgid "S3 endpoint is invalid: {0}"
+msgstr "S3終端節點無效:{0}"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:66
+msgid "S3 endpoint is required"
+msgstr "必須提供 S3 端點"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:70
+msgid "S3 endpoint URL"
+msgstr "S3 端點 URL"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:124
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "S3 path (e.g., backups/)"
+msgstr "S3 路徑(例如 backups/)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:108
+msgid "S3 Region"
+msgstr "S3 區域"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:113
+msgid "S3 region (e.g., us-east-1)"
+msgstr "S3 區域(例如 us-east-1)"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:92
+msgid "S3 secret access key"
+msgstr "S3 秘密存取金鑰"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:86
+msgid "S3 Secret Access Key"
+msgstr "S3 秘密存取金鑰"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:88
+msgid "S3 secret access key is required"
+msgstr "必須提供S3秘密存取金鑰"
+
+#: src/constants/errors/backup.ts:68
+msgid "S3 upload failed: {0}"
+msgstr "S3上傳失敗:{0}"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:32
+msgid "Saturday"
+msgstr "星期六"
+
 #: src/components/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3846,6 +4140,14 @@ msgstr "sbin 路徑不存在"
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "用手機掃描二維碼將賬戶新增到應用程式中。"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:114
+msgid "Schedule"
+msgstr "計劃"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:144
+msgid "Schedule Type"
+msgstr "計劃類型"
+
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgstr "SDK"
@@ -3871,7 +4173,7 @@ msgstr "安全設定"
 msgid "Security Token"
 msgstr "安全代碼"
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgstr "安全代碼資訊"
 
@@ -4126,6 +4428,29 @@ msgstr "已停止"
 msgid "Storage"
 msgstr "儲存空間"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:96
+msgid "Storage Configuration"
+msgstr "儲存配置"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:118
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:52
+msgid "Storage Path"
+msgstr "儲存路徑"
+
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:120
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:54
+msgid "Storage path is required"
+msgstr "儲存路徑是必填項"
+
+#: src/constants/errors/backup.ts:61
+msgid "Storage path not in granted access paths: {0}"
+msgstr "儲存路徑不在授予的存取路徑中: {0}"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:74
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:43
+msgid "Storage Type"
+msgstr "儲存類型"
+
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgstr "串流已啟用"
@@ -4150,10 +4475,17 @@ msgstr "streams-enabled 資料夾不存在"
 msgid "Stub Status Port"
 msgstr "存根狀態端口"
 
-#: src/constants/index.ts:25 src/views/notification/notificationColumns.tsx:35
+#: src/constants/index.ts:25 src/views/backup/AutoBackup/AutoBackup.vue:193
+#: src/views/backup/AutoBackup/AutoBackup.vue:218
+#: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgstr "成功"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:121
+#: src/views/backup/AutoBackup/components/CronEditor.vue:26
+msgid "Sunday"
+msgstr "星期日"
+
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
@@ -4265,7 +4597,7 @@ msgstr "同步"
 msgid "System"
 msgstr "系統"
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgstr "系統備份"
 
@@ -4282,7 +4614,6 @@ msgid "System Restore"
 msgstr "系統恢復"
 
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgstr "系統已成功恢復。"
 
@@ -4306,6 +4637,10 @@ msgstr "終端"
 msgid "Terminal Start Command"
 msgstr "終端機啟動指令"
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "測試 S3 連接"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
@@ -4458,7 +4793,7 @@ msgid ""
 "certificate files on the file system will not be deleted."
 msgstr "此操作僅會從資料庫中移除憑證。檔案系統上的憑證檔案不會被刪除。"
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
@@ -4490,6 +4825,10 @@ msgstr "這將在 %{nodeNames} 上升級或重新安裝 Nginx UI 到 %{version}
 msgid "Throttle"
 msgstr "節流"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "星期四"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4596,6 +4935,10 @@ msgstr "TOTP 是一種使用基於時間的一次性密碼演算法的多重因
 msgid "Trash"
 msgstr "垃圾桶"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "星期二"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgstr "需要多重因素驗證"
@@ -4612,6 +4955,10 @@ msgstr "類型"
 msgid "Unknown"
 msgstr "未知"
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "不支援的備份類型: {0}"
+
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgstr "更新密碼"
@@ -4624,6 +4971,7 @@ msgstr "更新資料"
 msgid "Update successfully"
 msgstr "更新成功"
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4636,7 +4984,7 @@ msgstr "更新成功"
 msgid "Updated at"
 msgstr "更新時間"
 
-#: src/routes/modules/system.ts:33
+#: src/routes/modules/system.ts:26
 #: src/views/environments/list/Environment.vue:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4766,10 +5114,10 @@ msgstr "已檢視"
 msgid "Waiting processes"
 msgstr "等待過程"
 
-#: src/constants/index.ts:23 src/views/config/InspectConfig.vue:33
+#: src/constants/index.ts:23 src/views/backup/components/BackupCreator.vue:138
+#: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgstr "警告"
 
@@ -4805,6 +5153,18 @@ msgstr "WebAuthn 設定尚未設定"
 msgid "WebSocket connection error"
 msgstr "WebSocket 連接錯誤"
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:29
+msgid "Wednesday"
+msgstr "星期三"
+
+#: src/views/backup/AutoBackup/components/CronEditor.vue:20
+msgid "Weekly"
+msgstr "每週"
+
+#: src/views/backup/AutoBackup/AutoBackup.vue:135
+msgid "Weekly on %{day} at %{time}"
+msgstr "每週%{day}的%{time}"
+
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4844,7 +5204,7 @@ msgstr "worker 行程"
 msgid "Workers"
 msgstr "worker"
 
-#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:56
+#: src/layouts/HeaderLayout.vue:62 src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgstr "工作區"
@@ -4922,6 +5282,9 @@ msgstr "您的舊代碼將不再有效。"
 msgid "Your passkeys"
 msgstr "您的通行金鑰"
 
+#~ msgid "Last Backup Error"
+#~ msgstr "最後一次備份錯誤"
+
 #~ msgid "Apply"
 #~ msgstr "應用"
 
@@ -4946,9 +5309,6 @@ msgstr "您的通行金鑰"
 #~ msgid "Ok"
 #~ msgstr "確定"
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "請填寫必填欄位"
-
 #~ msgid "Recover"
 #~ msgstr "恢復"
 
@@ -5023,9 +5383,6 @@ msgstr "您的通行金鑰"
 #~ msgid "Nginx Conf Include Streams Enabled"
 #~ msgstr "Nginx 設定檔包含 streams-enabled"
 
-#~ msgid "Sites Directory"
-#~ msgstr "Sites 資料夾"
-
 #~ msgid "Format error %{msg}"
 #~ msgstr "格式錯誤 %{msg}"
 

+ 2 - 1
app/src/routes/index.ts

@@ -4,9 +4,9 @@ import { useNProgress } from '@/lib/nprogress/nprogress'
 import { useUserStore } from '@/pinia'
 import { authRoutes } from './modules/auth'
 
+import { backupRoutes } from './modules/backup'
 import { certificatesRoutes } from './modules/certificates'
 import { configRoutes } from './modules/config'
-// Import module routes
 import { dashboardRoutes } from './modules/dashboard'
 import { environmentsRoutes } from './modules/environments'
 import { errorRoutes } from './modules/error'
@@ -33,6 +33,7 @@ const mainLayoutChildren: RouteRecordRaw[] = [
   ...notificationsRoutes,
   ...userRoutes,
   ...preferenceRoutes,
+  ...backupRoutes,
   ...systemRoutes,
 ]
 

+ 32 - 0
app/src/routes/modules/backup.ts

@@ -0,0 +1,32 @@
+import type { RouteRecordRaw } from 'vue-router'
+import { ClockCircleOutlined } from '@ant-design/icons-vue'
+
+export const backupRoutes: RouteRecordRaw[] = [
+  {
+    path: 'backup',
+    name: 'Backup',
+    component: () => import('@/layouts/BaseRouterView.vue'),
+    meta: {
+      icon: ClockCircleOutlined,
+      name: () => $gettext('Backup'),
+    },
+    children: [
+      {
+        path: 'backup-and-restore',
+        name: 'BackupAndRestore',
+        component: () => import('@/views/backup/index.vue'),
+        meta: {
+          name: () => $gettext('Backup'),
+        },
+      },
+      {
+        path: 'auto-backup',
+        name: 'AutoBackup',
+        component: () => import('@/views/backup/AutoBackup/AutoBackup.vue'),
+        meta: {
+          name: () => $gettext('Auto Backup'),
+        },
+      },
+    ],
+  },
+]

+ 0 - 7
app/src/routes/modules/system.ts

@@ -18,13 +18,6 @@ export const systemRoutes: RouteRecordRaw[] = [
       meta: {
         name: () => $gettext('Self Check'),
       },
-    }, {
-      path: 'backup',
-      name: 'Backup',
-      component: () => import('@/views/system/Backup/index.vue'),
-      meta: {
-        name: () => $gettext('Backup'),
-      },
     }, {
       path: 'upgrade',
       name: 'Upgrade',

+ 1 - 1
app/src/version.json

@@ -1 +1 @@
-{"version":"2.1.0-beta.1","build_id":1,"total_build":421}
+{"version":"2.1.0-beta.1","build_id":3,"total_build":423}

+ 259 - 0
app/src/views/backup/AutoBackup/AutoBackup.vue

@@ -0,0 +1,259 @@
+<script setup lang="tsx">
+import type { CustomRenderArgs, StdTableColumn } from '@uozi-admin/curd'
+import type { AutoBackup } from '@/api/backup'
+import { datetimeRender, StdCurd } from '@uozi-admin/curd'
+import { FormItem, Input, Tag } from 'ant-design-vue'
+import { autoBackup } from '@/api/backup'
+import { CronEditor, StorageConfigEditor } from './components'
+
+const columns: StdTableColumn[] = [
+  {
+    title: () => $gettext('Name'),
+    dataIndex: 'name',
+    sorter: true,
+    pure: true,
+    edit: {
+      type: 'input',
+      formItem: {
+        required: true,
+      },
+    },
+    search: true,
+  },
+  {
+    title: () => $gettext('Backup Type'),
+    dataIndex: 'backup_type',
+    customRender: ({ text }: CustomRenderArgs) => {
+      const typeMap = {
+        nginx_config: $gettext('Nginx Config'),
+        nginx_ui_config: $gettext('Nginx UI Config'),
+        both_config: $gettext('Both Config'),
+        custom_dir: $gettext('Custom Directory'),
+      }
+      return typeMap[text as keyof typeof typeMap] || text
+    },
+    edit: {
+      type: 'select',
+      formItem: {
+        required: true,
+      },
+      select: {
+        options: [
+          { label: $gettext('Nginx Config'), value: 'nginx_config' },
+          { label: $gettext('Nginx UI Config'), value: 'nginx_ui_config' },
+          { label: $gettext('Both Nginx and Nginx UI Config'), value: 'both_config' },
+          { label: $gettext('Custom Directory'), value: 'custom_dir' },
+        ],
+      },
+    },
+    search: true,
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Backup Path'),
+    dataIndex: 'backup_path',
+    edit: {
+      type: (formData: AutoBackup) => {
+        if (formData.backup_type !== 'custom_dir')
+          return <div />
+
+        return (
+          <FormItem class="mb-0" required={true} label={$gettext('Backup Path')}>
+            <Input v-model={formData.backup_path} />
+          </FormItem>
+        )
+      },
+      formItem: {
+        hiddenLabelInEdit: true,
+      },
+    },
+    hiddenInTable: true,
+  },
+  {
+    title: () => $gettext('Storage Type'),
+    dataIndex: 'storage_type',
+    customRender: ({ text }: CustomRenderArgs) => {
+      const typeMap = {
+        local: $gettext('Local'),
+        s3: $gettext('S3'),
+      }
+      return typeMap[text as keyof typeof typeMap] || text
+    },
+    search: {
+      type: 'select',
+      select: {
+        options: [
+          { label: $gettext('Local'), value: 'local' },
+          { label: $gettext('S3'), value: 's3' },
+        ],
+      },
+    },
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Storage Configuration'),
+    dataIndex: 'storage_config',
+    edit: {
+      type: (formData: AutoBackup) => {
+        return (
+          <div>
+            <div class="font-500 mb-4">{$gettext('Storage Configuration')}</div>
+            <StorageConfigEditor v-model={formData} storageType={formData.storage_type} />
+          </div>
+        )
+      },
+      formItem: {
+        hiddenLabelInEdit: true,
+      },
+    },
+    hiddenInTable: true,
+  },
+  {
+    title: () => $gettext('Schedule'),
+    dataIndex: 'cron_expression',
+    customRender: ({ text }: CustomRenderArgs) => {
+      if (!text)
+        return ''
+
+      // Parse and display human-readable format
+      const parts = text.trim().split(/\s+/)
+      if (parts.length !== 5)
+        return text
+
+      const [minute, hour, dayOfMonth, month, dayOfWeek] = parts
+      const timeStr = `${hour.padStart(2, '0')}:${minute.padStart(2, '0')}`
+
+      if (dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
+        return $gettext('Daily at %{time}', { time: timeStr })
+      }
+
+      if (dayOfMonth === '*' && month === '*' && dayOfWeek !== '*') {
+        const weekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
+        const dayName = weekDays[Number.parseInt(dayOfWeek)] || 'Sunday'
+        return $gettext('Weekly on %{day} at %{time}', { day: $gettext(dayName), time: timeStr })
+      }
+
+      if (dayOfMonth !== '*' && month === '*' && dayOfWeek === '*') {
+        return $gettext('Monthly on day %{day} at %{time}', { day: dayOfMonth, time: timeStr })
+      }
+
+      return text
+    },
+    edit: {
+      type: (formData: AutoBackup) => {
+        return (
+          <CronEditor v-model={formData.cron_expression} />
+        )
+      },
+      formItem: {
+        hiddenLabelInEdit: true,
+      },
+    },
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Enabled'),
+    dataIndex: 'enabled',
+    customRender: ({ text }: CustomRenderArgs) => {
+      return text
+        ? <Tag color="green">{$gettext('Enabled')}</Tag>
+        : <Tag color="red">{$gettext('Disabled')}</Tag>
+    },
+    edit: {
+      type: 'switch',
+    },
+    search: {
+      type: 'select',
+      select: {
+        options: [
+          { label: $gettext('Enabled'), value: 1 },
+          { label: $gettext('Disabled'), value: 0 },
+        ],
+      },
+    },
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Last Backup Time'),
+    dataIndex: 'last_backup_time',
+    customRender: datetimeRender,
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Last Backup Status'),
+    dataIndex: 'last_backup_status',
+    customRender: ({ text, record }: CustomRenderArgs) => {
+      const statusMap = {
+        pending: { color: 'orange', text: $gettext('Pending') },
+        success: { color: 'green', text: $gettext('Success') },
+        failed: { color: 'red', text: $gettext('Failed') },
+      }
+      const status = statusMap[text as keyof typeof statusMap]
+      const statusTag = status ? <Tag color={status.color}>{status.text}</Tag> : text
+
+      // Show error message below failed status
+      if (text === 'failed' && record.last_backup_error) {
+        return (
+          <div>
+            {statusTag}
+            <div class="text-red-500 text-xs mt-1 max-w-xs break-words">
+              {record.last_backup_error}
+            </div>
+          </div>
+        )
+      }
+
+      return statusTag
+    },
+    search: {
+      type: 'select',
+      select: {
+        options: [
+          { label: $gettext('Pending'), value: 'pending' },
+          { label: $gettext('Success'), value: 'success' },
+          { label: $gettext('Failed'), value: 'failed' },
+        ],
+      },
+    },
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Created at'),
+    dataIndex: 'created_at',
+    customRender: datetimeRender,
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Updated at'),
+    dataIndex: 'updated_at',
+    customRender: datetimeRender,
+    sorter: true,
+    pure: true,
+  },
+  {
+    title: () => $gettext('Actions'),
+    dataIndex: 'actions',
+    fixed: 'right',
+  },
+]
+</script>
+
+<template>
+  <StdCurd
+    :title="$gettext('Auto Backup')"
+    :columns="columns"
+    :api="autoBackup"
+    disable-export
+  />
+</template>
+
+<style lang="less">
+
+</style>

+ 207 - 0
app/src/views/backup/AutoBackup/components/CronEditor.vue

@@ -0,0 +1,207 @@
+<script setup lang="ts">
+const modelValue = defineModel<string>({ default: '' })
+
+interface CronConfig {
+  type: 'daily' | 'weekly' | 'monthly' | 'custom'
+  hour: number
+  minute: number
+  dayOfWeek?: number // 0-6, 0 = Sunday
+  dayOfMonth?: number // 1-31
+}
+
+const cronConfig = ref<CronConfig>({
+  type: 'daily',
+  hour: 0,
+  minute: 0,
+})
+
+const cronTypes = [
+  { label: $gettext('Daily'), value: 'daily' },
+  { label: $gettext('Weekly'), value: 'weekly' },
+  { label: $gettext('Monthly'), value: 'monthly' },
+//  { label: $gettext('Custom'), value: 'custom' },
+]
+
+const weekDays = [
+  { label: $gettext('Sunday'), value: 0 },
+  { label: $gettext('Monday'), value: 1 },
+  { label: $gettext('Tuesday'), value: 2 },
+  { label: $gettext('Wednesday'), value: 3 },
+  { label: $gettext('Thursday'), value: 4 },
+  { label: $gettext('Friday'), value: 5 },
+  { label: $gettext('Saturday'), value: 6 },
+]
+
+const customCronExpression = ref('')
+
+// Parse cron expression to config
+function parseCronExpression(cron: string) {
+  if (!cron)
+    return
+
+  const parts = cron.trim().split(/\s+/)
+  if (parts.length !== 5) {
+    cronConfig.value.type = 'custom'
+    customCronExpression.value = cron
+    return
+  }
+
+  const [minute, hour, dayOfMonth, month, dayOfWeek] = parts
+
+  cronConfig.value.minute = Number.parseInt(minute) || 0
+  cronConfig.value.hour = Number.parseInt(hour) || 0
+
+  // Check if it's a daily pattern (every day)
+  if (dayOfMonth === '*' && month === '*' && dayOfWeek === '*') {
+    cronConfig.value.type = 'daily'
+    return
+  }
+
+  // Check if it's a weekly pattern (specific day of week)
+  if (dayOfMonth === '*' && month === '*' && dayOfWeek !== '*') {
+    cronConfig.value.type = 'weekly'
+    cronConfig.value.dayOfWeek = Number.parseInt(dayOfWeek) || 0
+    return
+  }
+
+  // Check if it's a monthly pattern (specific day of month)
+  if (dayOfMonth !== '*' && month === '*' && dayOfWeek === '*') {
+    cronConfig.value.type = 'monthly'
+    cronConfig.value.dayOfMonth = Number.parseInt(dayOfMonth) || 1
+    return
+  }
+
+  // Otherwise, it's custom
+  cronConfig.value.type = 'custom'
+  customCronExpression.value = cron
+}
+
+// Generate cron expression from config
+function generateCronExpression() {
+  const { type, minute, hour, dayOfWeek, dayOfMonth } = cronConfig.value
+
+  switch (type) {
+    case 'daily':
+      return `${minute} ${hour} * * *`
+    case 'weekly':
+      return `${minute} ${hour} * * ${dayOfWeek ?? 0}`
+    case 'monthly':
+      return `${minute} ${hour} ${dayOfMonth ?? 1} * *`
+    case 'custom':
+      return customCronExpression.value
+    default:
+      return `${minute} ${hour} * * *`
+  }
+}
+
+// Watch for changes and update model value
+watch(cronConfig, () => {
+  if (cronConfig.value.type !== 'custom') {
+    modelValue.value = generateCronExpression()
+  }
+}, { deep: true })
+
+watch(customCronExpression, newValue => {
+  if (cronConfig.value.type === 'custom') {
+    modelValue.value = newValue
+  }
+})
+
+// Initialize from model value
+watch(modelValue, newValue => {
+  if (newValue) {
+    parseCronExpression(newValue)
+  }
+}, { immediate: true })
+
+// Human readable description
+const cronDescription = computed(() => {
+  const { type, hour, minute, dayOfWeek, dayOfMonth } = cronConfig.value
+  const timeStr = `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
+  const dayName = weekDays.find(d => d.value === dayOfWeek)?.label || $gettext('Sunday')
+
+  switch (type) {
+    case 'daily':
+      return $gettext('Execute on every day at %{time}', { time: timeStr })
+    case 'weekly':
+      return $gettext('Execute on every %{day} at %{time}', { day: dayName, time: timeStr })
+    case 'monthly':
+      return $gettext('Execute on every month on day %{day} at %{time}', { day: dayOfMonth?.toString() || '1', time: timeStr })
+    case 'custom':
+      return customCronExpression.value || $gettext('Custom cron expression')
+    default:
+      return ''
+  }
+})
+</script>
+
+<template>
+  <div>
+    <div class="font-500 mb-4">
+      {{ $gettext('Backup Schedule') }}
+    </div>
+
+    <AFormItem :label="$gettext('Schedule Type')">
+      <ASelect v-model:value="cronConfig.type" :options="cronTypes" />
+    </AFormItem>
+
+    <AAlert
+      v-if="cronDescription"
+      :message="cronDescription"
+      type="info"
+      show-icon
+      class="mb-4"
+    />
+
+    <template v-if="cronConfig.type !== 'custom'">
+      <ARow :gutter="16">
+        <ACol :span="12">
+          <AFormItem :label="$gettext('Hour')">
+            <AInputNumber
+              v-model:value="cronConfig.hour"
+              :min="0"
+              :max="23"
+              style="width: 100%"
+            />
+          </AFormItem>
+        </ACol>
+        <ACol :span="12">
+          <AFormItem :label="$gettext('Minute')">
+            <AInputNumber
+              v-model:value="cronConfig.minute"
+              :min="0"
+              :max="59"
+              style="width: 100%"
+            />
+          </AFormItem>
+        </ACol>
+      </ARow>
+
+      <AFormItem v-if="cronConfig.type === 'weekly'" :label="$gettext('Day of Week')">
+        <ASelect v-model:value="cronConfig.dayOfWeek" :options="weekDays" />
+      </AFormItem>
+
+      <AFormItem v-if="cronConfig.type === 'monthly'" :label="$gettext('Day of Month')">
+        <AInputNumber
+          v-model:value="cronConfig.dayOfMonth"
+          :min="1"
+          :max="31"
+          style="width: 100%"
+        />
+      </AFormItem>
+    </template>
+
+    <AFormItem v-if="cronConfig.type === 'custom'" :label="$gettext('Cron Expression')">
+      <AInput
+        v-model:value="customCronExpression"
+        :placeholder="$gettext('e.g., 0 0 * * * (daily at midnight)')"
+      />
+      <div class="mt-2 text-gray-500 text-sm">
+        {{ $gettext('Format: minute hour day month weekday') }}
+      </div>
+    </AFormItem>
+  </div>
+</template>
+
+<style scoped lang="less">
+</style>

+ 147 - 0
app/src/views/backup/AutoBackup/components/StorageConfigEditor.vue

@@ -0,0 +1,147 @@
+<script setup lang="ts">
+import type { AutoBackup } from '@/api/backup'
+
+import { CheckCircleOutlined, LoadingOutlined } from '@ant-design/icons-vue'
+import { message } from 'ant-design-vue'
+import { testS3Connection } from '@/api/backup'
+
+const modelValue = defineModel<AutoBackup>({ default: reactive({}) as AutoBackup })
+
+const isLocalStorage = computed(() => modelValue.value.storage_type === 'local')
+const isS3Storage = computed(() => modelValue.value.storage_type === 's3')
+const isTestingS3 = ref(false)
+
+onMounted(() => {
+  if (!modelValue.value.storage_type)
+    modelValue.value.storage_type = 'local'
+})
+
+async function handleTestS3Connection() {
+  if (!modelValue.value.s3_bucket || !modelValue.value.s3_access_key_id || !modelValue.value.s3_secret_access_key) {
+    message.warning($gettext('Please fill in required S3 configuration fields'))
+    return
+  }
+
+  isTestingS3.value = true
+  try {
+    await testS3Connection(modelValue.value)
+    message.success($gettext('S3 connection test successful'))
+  }
+  // eslint-disable-next-line ts/no-explicit-any
+  catch (error: any) {
+    const errorMessage = error?.response?.data?.error || error?.message || $gettext('S3 connection test failed')
+    message.error(errorMessage)
+  }
+  finally {
+    isTestingS3.value = false
+  }
+}
+</script>
+
+<template>
+  <div>
+    <AFormItem required :label="$gettext('Storage Type')">
+      <ASelect
+        v-model:value="modelValue.storage_type"
+        :options="[{ label: $gettext('Local'), value: 'local' },
+                   { label: $gettext('S3'), value: 's3' }]"
+      />
+    </AFormItem>
+    <AFormItem
+      v-if="isLocalStorage"
+      :label="$gettext('Storage Path')"
+      name="storage_path"
+      :rules="[{ required: true, message: $gettext('Storage path is required') }]"
+    >
+      <AInput
+        v-model:value="modelValue.storage_path"
+        :placeholder="isS3Storage ? $gettext('S3 path (e.g., backups/)') : $gettext('Local path (e.g., /var/backups)')"
+      />
+    </AFormItem>
+
+    <template v-else-if="isS3Storage">
+      <AFormItem
+        :label="$gettext('S3 Endpoint')"
+        name="s3_endpoint"
+        :rules="[{ required: true, message: $gettext('S3 endpoint is required') }]"
+      >
+        <AInput
+          v-model:value="modelValue.s3_endpoint"
+          :placeholder="$gettext('S3 endpoint URL')"
+        />
+      </AFormItem>
+
+      <AFormItem
+        :label="$gettext('S3 Access Key ID')"
+        name="s3_access_key_id"
+        :rules="[{ required: true, message: $gettext('S3 access key ID is required') }]"
+      >
+        <AInput
+          v-model:value="modelValue.s3_access_key_id"
+          :placeholder="$gettext('S3 access key ID')"
+        />
+      </AFormItem>
+
+      <AFormItem
+        :label="$gettext('S3 Secret Access Key')"
+        name="s3_secret_access_key"
+        :rules="[{ required: true, message: $gettext('S3 secret access key is required') }]"
+      >
+        <AInputPassword
+          v-model:value="modelValue.s3_secret_access_key"
+          :placeholder="$gettext('S3 secret access key')"
+        />
+      </AFormItem>
+
+      <AFormItem
+        :label="$gettext('S3 Bucket')"
+        name="s3_bucket"
+        :rules="[{ required: true, message: $gettext('S3 bucket is required') }]"
+      >
+        <AInput
+          v-model:value="modelValue.s3_bucket"
+          :placeholder="$gettext('S3 bucket name')"
+        />
+      </AFormItem>
+
+      <AFormItem
+        :label="$gettext('S3 Region')"
+        name="s3_region"
+      >
+        <AInput
+          v-model:value="modelValue.s3_region"
+          :placeholder="$gettext('S3 region (e.g., us-east-1)')"
+        />
+      </AFormItem>
+
+      <AFormItem
+        :label="$gettext('Storage Path')"
+        name="storage_path"
+        :rules="[{ required: true, message: $gettext('Storage path is required') }]"
+      >
+        <AInput
+          v-model:value="modelValue.storage_path"
+          :placeholder="$gettext('S3 path (e.g., backups/)')"
+        />
+      </AFormItem>
+
+      <AFormItem>
+        <AButton
+          type="primary"
+          ghost
+          :loading="isTestingS3"
+          @click="handleTestS3Connection"
+        >
+          <template #icon>
+            <CheckCircleOutlined v-if="!isTestingS3" />
+            <LoadingOutlined v-else />
+          </template>
+          {{ $gettext('Test S3 Connection') }}
+        </AButton>
+      </AFormItem>
+    </template>
+  </div>
+</template>
+
+<style scoped lang="less">
+</style>

+ 2 - 0
app/src/views/backup/AutoBackup/components/index.ts

@@ -0,0 +1,2 @@
+export { default as CronEditor } from './CronEditor.vue'
+export { default as StorageConfigEditor } from './StorageConfigEditor.vue'

+ 0 - 0
app/src/views/system/Backup/BackupCreator.vue → app/src/views/backup/components/BackupCreator.vue


+ 0 - 2
app/src/views/system/Backup/SystemRestore.vue → app/src/views/backup/components/SystemRestore.vue

@@ -3,8 +3,6 @@ import { message } from 'ant-design-vue'
 import SystemRestoreContent from '@/components/SystemRestore/SystemRestoreContent.vue'
 
 function handleRestoreSuccess(options: { restoreNginx: boolean, restoreNginxUI: boolean }): void {
-  message.success($gettext('System restored successfully.'))
-
   // Only redirect to login page if Nginx UI was restored
   if (options.restoreNginxUI) {
     message.info($gettext('Please log in.'))

+ 2 - 2
app/src/views/system/Backup/index.vue → app/src/views/backup/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
-import BackupCreator from './BackupCreator.vue'
-import SystemRestore from './SystemRestore.vue'
+import BackupCreator from './components/BackupCreator.vue'
+import SystemRestore from './components/SystemRestore.vue'
 </script>
 
 <template>

+ 3 - 2
docs/.vitepress/config/en.ts

@@ -45,18 +45,19 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           items: [
             { text: 'App', link: '/guide/config-app' },
-            { text: 'Server', link: '/guide/config-server' },
-            { text: 'Database', link: '/guide/config-database' },
             { text: 'Auth', link: '/guide/config-auth' },
+            { text: 'Backup', link: '/guide/config-backup' },
             { text: 'Casdoor', link: '/guide/config-casdoor' },
             { text: 'Cert', link: '/guide/config-cert' },
             { text: 'Cluster', link: '/guide/config-cluster' },
             { text: 'Crypto', link: '/guide/config-crypto' },
+            { text: 'Database', link: '/guide/config-database' },
             { text: 'Http', link: '/guide/config-http' },
             { text: 'Logrotate', link: '/guide/config-logrotate' },
             { text: 'Nginx', link: '/guide/config-nginx' },
             { text: 'Node', link: '/guide/config-node' },
             { text: 'Open AI', link: '/guide/config-openai' },
+            { text: 'Server', link: '/guide/config-server' },
             { text: 'Terminal', link: '/guide/config-terminal' },
             { text: 'Webauthn', link: '/guide/config-webauthn' }
           ]

+ 3 - 2
docs/.vitepress/config/zh_CN.ts

@@ -50,18 +50,19 @@ export const zhCNConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           items: [
             { text: 'App', link: '/zh_CN/guide/config-app' },
-            { text: 'Server', link: '/zh_CN/guide/config-server' },
-            { text: 'Database', link: '/zh_CN/guide/config-database' },
             { text: 'Auth', link: '/zh_CN/guide/config-auth' },
+            { text: 'Backup', link: '/zh_CN/guide/config-backup' },
             { text: 'Casdoor', link: '/zh_CN/guide/config-casdoor' },
             { text: 'Cert', link: '/zh_CN/guide/config-cert' },
             { text: 'Cluster', link: '/zh_CN/guide/config-cluster' },
             { text: 'Crypto', link: '/zh_CN/guide/config-crypto' },
+            { text: 'Database', link: '/zh_CN/guide/config-database' },
             { text: 'Http', link: '/zh_CN/guide/config-http' },
             { text: 'Logrotate', link: '/zh_CN/guide/config-logrotate' },
             { text: 'Nginx', link: '/zh_CN/guide/config-nginx' },
             { text: 'Node', link: '/zh_CN/guide/config-node' },
             { text: 'Open AI', link: '/zh_CN/guide/config-openai' },
+            { text: 'Server', link: '/zh_CN/guide/config-server' },
             { text: 'Terminal', link: '/zh_CN/guide/config-terminal' },
             { text: 'Webauthn', link: '/zh_CN/guide/config-webauthn' }
           ]

+ 3 - 2
docs/.vitepress/config/zh_TW.ts

@@ -50,18 +50,19 @@ export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           items: [
             { text: 'App', link: '/zh_TW/guide/config-app' },
-            { text: 'Server', link: '/zh_TW/guide/config-server' },
-            { text: 'Database', link: '/zh_TW/guide/config-database' },
             { text: 'Auth', link: '/zh_TW/guide/config-auth' },
+            { text: 'Backup', link: '/zh_TW/guide/config-backup' },
             { text: 'Casdoor', link: '/zh_TW/guide/config-casdoor' },
             { text: 'Cert', link: '/zh_TW/guide/config-cert' },
             { text: 'Cluster', link: '/zh_TW/guide/config-cluster' },
             { text: 'Crypto', link: '/zh_TW/guide/config-crypto' },
+            { text: 'Database', link: '/zh_TW/guide/config-database' },
             { text: 'Http', link: '/zh_TW/guide/config-http' },
             { text: 'Logrotate', link: '/zh_TW/guide/config-logrotate' },
             { text: 'Nginx', link: '/zh_TW/guide/config-nginx' },
             { text: 'Node', link: '/zh_TW/guide/config-node' },
             { text: 'Open AI', link: '/zh_TW/guide/config-openai' },
+            { text: 'Server', link: '/zh_TW/guide/config-server' },
             { text: 'Terminal', link: '/zh_TW/guide/config-terminal' },
             { text: 'Webauthn', link: '/zh_TW/guide/config-webauthn' }
           ]

+ 104 - 0
docs/guide/config-backup.md

@@ -0,0 +1,104 @@
+# Backup Configuration
+
+The backup section of the Nginx UI configuration controls the security and access permissions for backup operations. This section ensures that backup functionality operates within defined security boundaries while providing flexible storage options.
+
+## Overview
+
+Nginx UI provides comprehensive backup functionality that includes:
+
+- **Manual Backup**: On-demand backup creation through the web interface
+- **Automatic Backup**: Scheduled backup tasks with visual cron editor
+- **Multiple Backup Types**: Support for Nginx configuration, Nginx UI configuration, or custom directory backups
+- **Storage Options**: Local storage and S3-compatible object storage
+- **Security**: Encrypted backups with AES encryption for configuration data
+
+## GrantedAccessPath
+
+- Type: `[]string`
+- Default: `[]` (empty array)
+- Version: `>= v2.0.0`
+
+This is the most critical security setting for backup operations. It defines a list of directory paths that are allowed for backup operations, including both backup source paths and storage destination paths.
+
+### Purpose
+
+The `GrantedAccessPath` setting serves as a security boundary that:
+
+- **Prevents Unauthorized Access**: Restricts backup operations to explicitly authorized directories
+- **Protects System Files**: Prevents accidental backup or access to sensitive system directories
+- **Enforces Access Control**: Ensures all backup paths are within administrator-defined boundaries
+- **Prevents Path Traversal**: Blocks attempts to access directories outside the allowed scope
+
+### Configuration Format
+
+```ini
+[backup]
+GrantedAccessPath = /var/backups
+GrantedAccessPath = /home/user/backups
+```
+
+### Path Validation Rules
+
+1. **Prefix Matching**: Paths are validated using prefix matching with proper boundary checking
+2. **Path Cleaning**: All paths are normalized to prevent directory traversal attacks (e.g., `../` sequences)
+3. **Exact Boundaries**: `/tmp` allows `/tmp/backup` but not `/tmpfoo` to prevent confusion
+4. **Empty Default**: By default, no custom directory backup operations are allowed for maximum security
+
+### Security Considerations
+
+- **Default Security**: The default empty configuration ensures no custom directory backup operations are allowed until explicitly configured
+- **Explicit Configuration**: Administrators must consciously define allowed paths
+- **Regular Review**: Periodically review and update allowed paths based on operational needs
+- **Minimal Permissions**: Only grant access to directories that genuinely need backup functionality
+
+## Backup Types
+
+### Configuration Backups
+
+When backing up Nginx or Nginx UI configurations:
+
+- **Encryption**: All configuration backups are automatically encrypted using AES encryption
+- **Key Management**: Encryption keys are generated automatically and saved alongside backup files
+- **Integrity Verification**: SHA-256 hashes ensure backup integrity
+- **Metadata**: Version information and timestamps are included for restoration context
+
+### Custom Directory Backups
+
+For custom directory backups:
+
+- **No Encryption**: Custom directory backups are stored as standard ZIP files without encryption
+- **Path Validation**: Source directories must be within `GrantedAccessPath` boundaries
+- **Flexible Content**: Can backup any directory structure within allowed paths
+
+## Storage Configuration
+
+### Local Storage
+
+- **Path Validation**: Storage paths must be within `GrantedAccessPath` boundaries
+- **Directory Creation**: Storage directories are created automatically if they don't exist
+- **Permissions**: Backup files are created with secure permissions (0600)
+
+### S3 Storage
+
+For S3-compatible object storage:
+
+- **Required Fields**: Bucket name, access key ID, and secret access key are mandatory
+- **Optional Fields**: Endpoint URL and region can be configured for custom S3 providers
+- **Future Enhancement**: S3 upload functionality is planned for future releases
+
+## Automatic Backup Scheduling
+
+### Visual Cron Editor
+
+Automatic backups use a visual cron editor interface that allows you to:
+
+- **Select Frequency**: Choose from daily, weekly, monthly, or custom schedules
+- **Set Time**: Pick specific hours and minutes for backup execution
+- **Preview Schedule**: View human-readable descriptions of the backup schedule
+
+### Task Management
+
+- **Status Tracking**: Each backup task tracks execution status (pending, success, failed)
+- **Error Logging**: Failed backups include detailed error messages for troubleshooting
+
+This configuration enables backup operations while maintaining strict security boundaries, ensuring that backup functionality cannot be misused to access unauthorized system areas. 

+ 104 - 0
docs/zh_CN/guide/config-backup.md

@@ -0,0 +1,104 @@
+# 备份配置
+
+Nginx UI 配置中的备份部分控制备份操作的安全性和访问权限。此部分确保备份功能在定义的安全边界内运行,同时提供灵活的存储选项。
+
+## 概述
+
+Nginx UI 提供全面的备份功能,包括:
+
+- **手动备份**:通过 Web 界面按需创建备份
+- **自动备份**:使用可视化 cron 编辑器的定时备份任务
+- **多种备份类型**:支持 Nginx 配置、Nginx UI 配置或自定义目录备份
+- **存储选项**:本地存储和 S3 兼容的对象存储
+- **安全性**:配置数据使用 AES 加密的加密备份
+
+## GrantedAccessPath
+
+- 类型:`[]string`
+- 默认值:`[]`(空数组)
+- 版本:`>= v2.0.0`
+
+这是备份操作最关键的安全设置。它定义了允许进行备份操作的目录路径列表,包括备份源路径和存储目标路径。
+
+### 用途
+
+`GrantedAccessPath` 设置作为安全边界:
+
+- **防止未授权访问**:将备份操作限制在明确授权的目录内
+- **保护系统文件**:防止意外备份或访问敏感的系统目录
+- **强制访问控制**:确保所有备份路径都在管理员定义的边界内
+- **防止路径遍历**:阻止尝试访问允许范围外的目录
+
+### 配置格式
+
+```ini
+[backup]
+GrantedAccessPath = /var/backups
+GrantedAccessPath = /home/user/backups
+```
+
+### 路径验证规则
+
+1. **前缀匹配**:使用适当的边界检查进行前缀匹配验证路径
+2. **路径清理**:所有路径都经过标准化处理以防止目录遍历攻击(如 `../` 序列)
+3. **精确边界**:`/tmp` 允许 `/tmp/backup` 但不允许 `/tmpfoo` 以防止混淆
+4. **空默认值**:默认情况下,为了最大安全性,不允许任何自定义目录备份操作
+
+### 安全考虑
+
+- **默认安全**:默认的空配置确保在明确配置之前不允许任何自定义目录备份操作
+- **显式配置**:管理员必须有意识地定义允许的路径
+- **定期审查**:根据操作需要定期审查和更新允许的路径
+- **最小权限**:仅授予真正需要备份功能的目录访问权限
+
+## 备份类型
+
+### 配置备份
+
+备份 Nginx 或 Nginx UI 配置时:
+
+- **加密**:所有配置备份都使用 AES 加密自动加密
+- **密钥管理**:加密密钥自动生成并与备份文件一起保存
+- **完整性验证**:SHA-256 哈希确保备份完整性
+- **元数据**:包含版本信息和时间戳以提供恢复上下文
+
+### 自定义目录备份
+
+对于自定义目录备份:
+
+- **无加密**:自定义目录备份存储为标准 ZIP 文件,不加密
+- **路径验证**:源目录必须在 `GrantedAccessPath` 边界内
+- **灵活内容**:可以备份允许路径内的任何目录结构
+
+## 存储配置
+
+### 本地存储
+
+- **路径验证**:存储路径必须在 `GrantedAccessPath` 边界内
+- **目录创建**:如果存储目录不存在,会自动创建
+- **权限**:备份文件使用安全权限(0600)创建
+
+### S3 存储
+
+对于 S3 兼容的对象存储:
+
+- **必需字段**:存储桶名称、访问密钥 ID 和秘密访问密钥是必需的
+- **可选字段**:可以为自定义 S3 提供商配置端点 URL 和区域
+- **未来增强**:计划在未来版本中实现 S3 上传功能
+
+## 自动备份调度
+
+### 可视化 Cron 编辑器
+
+自动备份使用可视化 cron 编辑器界面,允许您:
+
+- **选择频率**:从每日、每周、每月或自定义计划中选择
+- **设置时间**:选择备份执行的具体小时和分钟
+- **预览计划**:查看备份计划的人类可读描述
+
+### 任务管理
+
+- **状态跟踪**:每个备份任务跟踪执行状态(待处理、成功、失败)
+- **错误日志**:失败的备份包含详细的错误消息以便故障排除
+
+此配置在保持严格安全边界的同时启用备份操作,确保备份功能不会被滥用来访问未授权的系统区域。 

+ 104 - 0
docs/zh_TW/guide/config-backup.md

@@ -0,0 +1,104 @@
+# 備份配置
+
+Nginx UI 配置中的備份部分控制備份操作的安全性和存取權限。此部分確保備份功能在定義的安全邊界內運行,同時提供靈活的儲存選項。
+
+## 概述
+
+Nginx UI 提供全面的備份功能,包括:
+
+- **手動備份**:透過 Web 介面按需建立備份
+- **自動備份**:使用可視化 cron 編輯器的定時備份任務
+- **多種備份類型**:支援 Nginx 配置、Nginx UI 配置或自訂目錄備份
+- **儲存選項**:本地儲存和 S3 相容的物件儲存
+- **安全性**:配置資料使用 AES 加密的加密備份
+
+## GrantedAccessPath
+
+- 類型:`[]string`
+- 預設值:`[]`(空陣列)
+- 版本:`>= v2.0.0`
+
+這是備份操作最關鍵的安全設定。它定義了允許進行備份操作的目錄路徑清單,包括備份來源路徑和儲存目標路徑。
+
+### 用途
+
+`GrantedAccessPath` 設定作為安全邊界:
+
+- **防止未授權存取**:將備份操作限制在明確授權的目錄內
+- **保護系統檔案**:防止意外備份或存取敏感的系統目錄
+- **強制存取控制**:確保所有備份路徑都在管理員定義的邊界內
+- **防止路徑遍歷**:阻止嘗試存取允許範圍外的目錄
+
+### 配置格式
+
+```ini
+[backup]
+GrantedAccessPath = /var/backups
+GrantedAccessPath = /home/user/backups
+```
+
+### 路徑驗證規則
+
+1. **前綴比對**:使用適當的邊界檢查進行前綴比對驗證路徑
+2. **路徑清理**:所有路徑都經過標準化處理以防止目錄遍歷攻擊(如 `../` 序列)
+3. **精確邊界**:`/tmp` 允許 `/tmp/backup` 但不允許 `/tmpfoo` 以防止混淆
+4. **空預設值**:預設情況下,為了最大安全性,不允許任何自訂目錄備份操作
+
+### 安全考量
+
+- **預設安全**:預設的空配置確保在明確配置之前不允許任何自訂目錄備份操作
+- **明確配置**:管理員必須有意識地定義允許的路徑
+- **定期審查**:根據操作需要定期審查和更新允許的路徑
+- **最小權限**:僅授予真正需要備份功能的目錄存取權限
+
+## 備份類型
+
+### 配置備份
+
+備份 Nginx 或 Nginx UI 配置時:
+
+- **加密**:所有配置備份都使用 AES 加密自動加密
+- **金鑰管理**:加密金鑰自動產生並與備份檔案一起儲存
+- **完整性驗證**:SHA-256 雜湊確保備份完整性
+- **中繼資料**:包含版本資訊和時間戳記以提供還原上下文
+
+### 自訂目錄備份
+
+對於自訂目錄備份:
+
+- **無加密**:自訂目錄備份儲存為標準 ZIP 檔案,不加密
+- **路徑驗證**:來源目錄必須在 `GrantedAccessPath` 邊界內
+- **靈活內容**:可以備份允許路徑內的任何目錄結構
+
+## 儲存配置
+
+### 本地儲存
+
+- **路徑驗證**:儲存路徑必須在 `GrantedAccessPath` 邊界內
+- **目錄建立**:如果儲存目錄不存在,會自動建立
+- **權限**:備份檔案使用安全權限(0600)建立
+
+### S3 儲存
+
+對於 S3 相容的物件儲存:
+
+- **必需欄位**:儲存桶名稱、存取金鑰 ID 和秘密存取金鑰是必需的
+- **可選欄位**:可以為自訂 S3 提供商配置端點 URL 和區域
+- **未來增強**:計劃在未來版本中實現 S3 上傳功能
+
+## 自動備份排程
+
+### 可視化 Cron 編輯器
+
+自動備份使用可視化 cron 編輯器介面,允許您:
+
+- **選擇頻率**:從每日、每週、每月或自訂計劃中選擇
+- **設定時間**:選擇備份執行的具體小時和分鐘
+- **預覽計劃**:檢視備份計劃的人類可讀描述
+
+### 任務管理
+
+- **狀態追蹤**:每個備份任務追蹤執行狀態(待處理、成功、失敗)
+- **錯誤日誌**:失敗的備份包含詳細的錯誤訊息以便故障排除
+
+此配置在保持嚴格安全邊界的同時啟用備份操作,確保備份功能不會被濫用來存取未授權的系統區域。 

+ 5 - 0
go.mod

@@ -81,16 +81,21 @@ require (
 	github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
 	github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
 	github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
+	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
 	github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
 	github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 // indirect
 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
 	github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 // indirect
 	github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 // indirect
+	github.com/aws/aws-sdk-go-v2/service/s3 v1.79.4 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect

+ 10 - 0
go.sum

@@ -715,6 +715,8 @@ github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm
 github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
 github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
 github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
 github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
 github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
 github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
@@ -727,15 +729,23 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0io
 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
 github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 h1:BCG7DCXEXpNCcpwCxg1oi9pkJWH2+eZzTn9MY56MbVw=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
 github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2 h1:Bz0MltpmIFP2EBYADc17VHdXYxZw9JPQl8Ksq+w6aEE=
 github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.2/go.mod h1:Qy22QnQSdHbZwMZrarsWZBIuK51isPlkD+Z4sztxX0o=
 github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1 h1:41HrH51fydStW2Tah74zkqZlJfyx4gXeuGOdsIFuckY=
 github.com/aws/aws-sdk-go-v2/service/route53 v1.51.1/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.79.4 h1:4yxno6bNHkekkfqG/a1nz/gC2gBwhJSojV1+oTE7K+4=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.79.4/go.mod h1:qbn305Je/IofWBJ4bJz/Q7pDEtnnoInw/dGt71v6rHE=
 github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
 github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=

+ 539 - 0
internal/backup/auto_backup.go

@@ -0,0 +1,539 @@
+package backup
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/0xJacky/Nginx-UI/internal/notification"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/uozi-tech/cosy"
+	"github.com/uozi-tech/cosy/logger"
+)
+
+// BackupExecutionResult contains the result of a backup execution
+type BackupExecutionResult struct {
+	FilePath string // Path to the created backup file
+	KeyPath  string // Path to the encryption key file (if applicable)
+}
+
+// ExecuteAutoBackup executes an automatic backup task based on the configuration.
+// This function handles all types of backup operations and manages the backup status
+// throughout the execution process.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration to execute
+//
+// Returns:
+//   - error: CosyError if backup execution fails, nil if successful
+func ExecuteAutoBackup(autoBackup *model.AutoBackup) error {
+	logger.Infof("Starting auto backup task: %s (ID: %d, Type: %s, Storage: %s)",
+		autoBackup.Name, autoBackup.ID, autoBackup.BackupType, autoBackup.StorageType)
+
+	// Validate storage configuration before starting backup
+	if err := validateStorageConfiguration(autoBackup); err != nil {
+		logger.Errorf("Storage configuration validation failed for task %s: %v", autoBackup.Name, err)
+		updateBackupStatus(autoBackup.ID, model.BackupStatusFailed, err.Error())
+		// Send validation failure notification
+		notification.Error(
+			fmt.Sprintf("Auto Backup Configuration Error: %s", autoBackup.Name),
+			fmt.Sprintf("Storage configuration validation failed for backup task '%s'", autoBackup.Name),
+			map[string]interface{}{
+				"backup_id":   autoBackup.ID,
+				"backup_name": autoBackup.Name,
+				"error":       err.Error(),
+				"timestamp":   time.Now(),
+			},
+		)
+		return err
+	}
+
+	// Update backup status to pending
+	if err := updateBackupStatus(autoBackup.ID, model.BackupStatusPending, ""); err != nil {
+		logger.Errorf("Failed to update backup status to pending: %v", err)
+		return cosy.WrapErrorWithParams(ErrAutoBackupWriteFile, err.Error())
+	}
+
+	// Execute backup based on type
+	result, backupErr := executeBackupByType(autoBackup)
+
+	// Update backup status based on execution result
+	now := time.Now()
+	if backupErr != nil {
+		logger.Errorf("Auto backup task %s failed: %v", autoBackup.Name, backupErr)
+		if updateErr := updateBackupStatusWithTime(autoBackup.ID, model.BackupStatusFailed, backupErr.Error(), &now); updateErr != nil {
+			logger.Errorf("Failed to update backup status to failed: %v", updateErr)
+		}
+		// Send failure notification
+		notification.Error(
+			fmt.Sprintf("Auto Backup Failed: %s", autoBackup.Name),
+			fmt.Sprintf("Backup task '%s' failed to execute", autoBackup.Name),
+			map[string]interface{}{
+				"backup_id":   autoBackup.ID,
+				"backup_name": autoBackup.Name,
+				"error":       backupErr.Error(),
+				"timestamp":   now,
+			},
+		)
+		return backupErr
+	}
+
+	// Handle storage upload based on storage type
+	if uploadErr := handleBackupStorage(autoBackup, result); uploadErr != nil {
+		logger.Errorf("Auto backup storage upload failed for task %s: %v", autoBackup.Name, uploadErr)
+		if updateErr := updateBackupStatusWithTime(autoBackup.ID, model.BackupStatusFailed, uploadErr.Error(), &now); updateErr != nil {
+			logger.Errorf("Failed to update backup status to failed: %v", updateErr)
+		}
+		// Send storage failure notification
+		notification.Error(
+			fmt.Sprintf("Auto Backup Storage Failed: %s", autoBackup.Name),
+			fmt.Sprintf("Backup task '%s' failed during storage upload", autoBackup.Name),
+			map[string]interface{}{
+				"backup_id":   autoBackup.ID,
+				"backup_name": autoBackup.Name,
+				"error":       uploadErr.Error(),
+				"timestamp":   now,
+			},
+		)
+		return uploadErr
+	}
+
+	logger.Infof("Auto backup task %s completed successfully, file: %s", autoBackup.Name, result.FilePath)
+	if updateErr := updateBackupStatusWithTime(autoBackup.ID, model.BackupStatusSuccess, "", &now); updateErr != nil {
+		logger.Errorf("Failed to update backup status to success: %v", updateErr)
+	}
+
+	// Send success notification
+	notification.Success(
+		fmt.Sprintf("Auto Backup Completed: %s", autoBackup.Name),
+		fmt.Sprintf("Backup task '%s' completed successfully", autoBackup.Name),
+		map[string]interface{}{
+			"backup_id":   autoBackup.ID,
+			"backup_name": autoBackup.Name,
+			"file_path":   result.FilePath,
+			"timestamp":   now,
+		},
+	)
+
+	return nil
+}
+
+// executeBackupByType executes the backup operation based on the backup type.
+// This function centralizes the backup type routing logic.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration
+//
+// Returns:
+//   - BackupExecutionResult: Result containing file paths
+//   - error: CosyError if backup fails
+func executeBackupByType(autoBackup *model.AutoBackup) (*BackupExecutionResult, error) {
+	switch autoBackup.BackupType {
+	case model.BackupTypeNginxConfig:
+		return createEncryptedBackup(autoBackup, "nginx_config")
+	case model.BackupTypeNginxUIConfig:
+		return createEncryptedBackup(autoBackup, "nginx_ui_config")
+	case model.BackupTypeBothConfig:
+		return createEncryptedBackup(autoBackup, "both_config")
+	case model.BackupTypeCustomDir:
+		return createCustomDirectoryBackup(autoBackup)
+	default:
+		return nil, cosy.WrapErrorWithParams(ErrAutoBackupUnsupportedType, string(autoBackup.BackupType))
+	}
+}
+
+// createEncryptedBackup creates an encrypted backup for Nginx/Nginx UI configurations.
+// This function handles all configuration backup types that require encryption.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration
+//   - backupPrefix: Prefix for the backup filename
+//
+// Returns:
+//   - BackupExecutionResult: Result containing file paths
+//   - error: CosyError if backup creation fails
+func createEncryptedBackup(autoBackup *model.AutoBackup, backupPrefix string) (*BackupExecutionResult, error) {
+	// Generate unique filename with timestamp
+	filename := fmt.Sprintf("%s_%s_%d.zip", backupPrefix, autoBackup.Name, time.Now().Unix())
+
+	// Determine output path based on storage type
+	var outputPath string
+	if autoBackup.StorageType == model.StorageTypeS3 {
+		// For S3 storage, create temporary file
+		tempDir := os.TempDir()
+		outputPath = filepath.Join(tempDir, filename)
+	} else {
+		// For local storage, use the configured storage path
+		outputPath = filepath.Join(autoBackup.StoragePath, filename)
+	}
+
+	// Create backup using the main backup function
+	backupResult, err := Backup()
+	if err != nil {
+		return nil, cosy.WrapErrorWithParams(ErrBackupNginx, err.Error())
+	}
+
+	// Write encrypted backup content to file
+	if err := writeBackupFile(outputPath, backupResult.BackupContent); err != nil {
+		return nil, err
+	}
+
+	// Create and write encryption key file
+	keyPath := outputPath + ".key"
+	if err := writeKeyFile(keyPath, backupResult.AESKey, backupResult.AESIv); err != nil {
+		return nil, err
+	}
+
+	return &BackupExecutionResult{
+		FilePath: outputPath,
+		KeyPath:  keyPath,
+	}, nil
+}
+
+// createCustomDirectoryBackup creates an unencrypted backup of a custom directory.
+// This function handles custom directory backups which are stored as plain ZIP files.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration
+//
+// Returns:
+//   - BackupExecutionResult: Result containing file paths
+//   - error: CosyError if backup creation fails
+func createCustomDirectoryBackup(autoBackup *model.AutoBackup) (*BackupExecutionResult, error) {
+	// Validate that backup path is specified for custom directory backup
+	if autoBackup.BackupPath == "" {
+		return nil, ErrAutoBackupPathRequired
+	}
+
+	// Validate backup source path
+	if err := ValidateBackupPath(autoBackup.BackupPath); err != nil {
+		return nil, err
+	}
+
+	// Generate unique filename with timestamp
+	filename := fmt.Sprintf("custom_dir_%s_%d.zip", autoBackup.Name, time.Now().Unix())
+
+	// Determine output path based on storage type
+	var outputPath string
+	if autoBackup.StorageType == model.StorageTypeS3 {
+		// For S3 storage, create temporary file
+		tempDir := os.TempDir()
+		outputPath = filepath.Join(tempDir, filename)
+	} else {
+		// For local storage, use the configured storage path
+		outputPath = filepath.Join(autoBackup.StoragePath, filename)
+	}
+
+	// Create unencrypted ZIP archive of the custom directory
+	if err := createZipArchive(outputPath, autoBackup.BackupPath); err != nil {
+		return nil, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
+	}
+
+	return &BackupExecutionResult{
+		FilePath: outputPath,
+		KeyPath:  "", // No key file for unencrypted backups
+	}, nil
+}
+
+// writeBackupFile writes backup content to the specified file path with proper permissions.
+// This function ensures backup files are created with secure permissions.
+//
+// Parameters:
+//   - filePath: Destination file path
+//   - content: Backup content to write
+//
+// Returns:
+//   - error: CosyError if file writing fails
+func writeBackupFile(filePath string, content []byte) error {
+	if err := os.WriteFile(filePath, content, 0600); err != nil {
+		return cosy.WrapErrorWithParams(ErrAutoBackupWriteFile, err.Error())
+	}
+	return nil
+}
+
+// writeKeyFile writes encryption key information to a key file.
+// This function creates a key file containing AES key and IV for encrypted backups.
+//
+// Parameters:
+//   - keyPath: Path for the key file
+//   - aesKey: Base64 encoded AES key
+//   - aesIv: Base64 encoded AES initialization vector
+//
+// Returns:
+//   - error: CosyError if key file writing fails
+func writeKeyFile(keyPath, aesKey, aesIv string) error {
+	keyContent := fmt.Sprintf("AES_KEY=%s\nAES_IV=%s\n", aesKey, aesIv)
+	if err := os.WriteFile(keyPath, []byte(keyContent), 0600); err != nil {
+		return cosy.WrapErrorWithParams(ErrAutoBackupWriteKeyFile, err.Error())
+	}
+	return nil
+}
+
+// updateBackupStatus updates the backup status in the database.
+// This function provides a centralized way to update backup execution status.
+//
+// Parameters:
+//   - id: Auto backup configuration ID
+//   - status: New backup status
+//   - errorMsg: Error message (empty for successful backups)
+//
+// Returns:
+//   - error: Database error if update fails
+func updateBackupStatus(id uint64, status model.BackupStatus, errorMsg string) error {
+	_, err := query.AutoBackup.Where(query.AutoBackup.ID.Eq(id)).Updates(map[string]interface{}{
+		"last_backup_status": status,
+		"last_backup_error":  errorMsg,
+	})
+	return err
+}
+
+// updateBackupStatusWithTime updates the backup status and timestamp in the database.
+// This function updates both status and execution time for completed backup operations.
+//
+// Parameters:
+//   - id: Auto backup configuration ID
+//   - status: New backup status
+//   - errorMsg: Error message (empty for successful backups)
+//   - backupTime: Timestamp of the backup execution
+//
+// Returns:
+//   - error: Database error if update fails
+func updateBackupStatusWithTime(id uint64, status model.BackupStatus, errorMsg string, backupTime *time.Time) error {
+	_, err := query.AutoBackup.Where(query.AutoBackup.ID.Eq(id)).Updates(map[string]interface{}{
+		"last_backup_status": status,
+		"last_backup_error":  errorMsg,
+		"last_backup_time":   backupTime,
+	})
+	return err
+}
+
+// GetEnabledAutoBackups retrieves all enabled auto backup configurations from the database.
+// This function is used by the cron scheduler to get active backup tasks.
+//
+// Returns:
+//   - []*model.AutoBackup: List of enabled auto backup configurations
+//   - error: Database error if query fails
+func GetEnabledAutoBackups() ([]*model.AutoBackup, error) {
+	return query.AutoBackup.Where(query.AutoBackup.Enabled.Is(true)).Find()
+}
+
+// GetAutoBackupByID retrieves a specific auto backup configuration by its ID.
+// This function provides access to individual backup configurations.
+//
+// Parameters:
+//   - id: Auto backup configuration ID
+//
+// Returns:
+//   - *model.AutoBackup: The auto backup configuration
+//   - error: Database error if query fails or record not found
+func GetAutoBackupByID(id uint64) (*model.AutoBackup, error) {
+	return query.AutoBackup.Where(query.AutoBackup.ID.Eq(id)).First()
+}
+
+// validateStorageConfiguration validates the storage configuration based on storage type.
+// This function centralizes storage validation logic for both local and S3 storage.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration to validate
+//
+// Returns:
+//   - error: CosyError if validation fails, nil if configuration is valid
+func validateStorageConfiguration(autoBackup *model.AutoBackup) error {
+	switch autoBackup.StorageType {
+	case model.StorageTypeLocal:
+		// For local storage, validate the storage path
+		return ValidateStoragePath(autoBackup.StoragePath)
+	case model.StorageTypeS3:
+		// For S3 storage, test the connection
+		s3Client, err := NewS3Client(autoBackup)
+		if err != nil {
+			return err
+		}
+		return s3Client.TestS3Connection(context.Background())
+	default:
+		return cosy.WrapErrorWithParams(ErrAutoBackupUnsupportedType, string(autoBackup.StorageType))
+	}
+}
+
+// handleBackupStorage handles the storage of backup files based on storage type.
+// This function routes backup storage to the appropriate handler (local or S3).
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration
+//   - result: The backup execution result containing file paths
+//
+// Returns:
+//   - error: CosyError if storage operation fails
+func handleBackupStorage(autoBackup *model.AutoBackup, result *BackupExecutionResult) error {
+	switch autoBackup.StorageType {
+	case model.StorageTypeLocal:
+		// For local storage, files are already written to the correct location
+		logger.Infof("Backup files stored locally: %s", result.FilePath)
+		return nil
+	case model.StorageTypeS3:
+		// For S3 storage, upload files to S3 and optionally clean up local files
+		return handleS3Storage(autoBackup, result)
+	default:
+		return cosy.WrapErrorWithParams(ErrAutoBackupUnsupportedType, string(autoBackup.StorageType))
+	}
+}
+
+// handleS3Storage handles S3 storage operations for backup files.
+// This function uploads backup files to S3 and manages local file cleanup.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration
+//   - result: The backup execution result containing file paths
+//
+// Returns:
+//   - error: CosyError if S3 operations fail
+func handleS3Storage(autoBackup *model.AutoBackup, result *BackupExecutionResult) error {
+	// Create S3 client
+	s3Client, err := NewS3Client(autoBackup)
+	if err != nil {
+		return err
+	}
+
+	// Upload backup files to S3
+	ctx := context.Background()
+	if err := s3Client.UploadBackupFiles(ctx, result, autoBackup); err != nil {
+		return err
+	}
+
+	// Clean up local files after successful S3 upload
+	if err := cleanupLocalBackupFiles(result); err != nil {
+		logger.Warnf("Failed to cleanup local backup files: %v", err)
+		// Don't return error for cleanup failure as the backup was successful
+	}
+
+	logger.Infof("Backup files successfully uploaded to S3 and local files cleaned up")
+	return nil
+}
+
+// cleanupLocalBackupFiles removes local backup files after successful S3 upload.
+// This function helps manage disk space by removing temporary local files.
+//
+// Parameters:
+//   - result: The backup execution result containing file paths to clean up
+//
+// Returns:
+//   - error: Standard error if cleanup fails
+func cleanupLocalBackupFiles(result *BackupExecutionResult) error {
+	// Remove backup file
+	if err := os.Remove(result.FilePath); err != nil && !os.IsNotExist(err) {
+		return fmt.Errorf("failed to remove backup file %s: %v", result.FilePath, err)
+	}
+
+	// Remove key file if it exists
+	if result.KeyPath != "" {
+		if err := os.Remove(result.KeyPath); err != nil && !os.IsNotExist(err) {
+			return fmt.Errorf("failed to remove key file %s: %v", result.KeyPath, err)
+		}
+	}
+
+	return nil
+}
+
+// ValidateAutoBackupConfig performs comprehensive validation of auto backup configuration.
+// This function centralizes all validation logic for both creation and modification.
+//
+// Parameters:
+//   - config: Auto backup configuration to validate
+//
+// Returns:
+//   - error: CosyError if validation fails, nil if configuration is valid
+func ValidateAutoBackupConfig(config *model.AutoBackup) error {
+	// Validate backup path for custom directory backup type
+	if config.BackupType == model.BackupTypeCustomDir {
+		if config.BackupPath == "" {
+			return ErrAutoBackupPathRequired
+		}
+
+		// Use centralized path validation from backup package
+		if err := ValidateBackupPath(config.BackupPath); err != nil {
+			return err
+		}
+	}
+
+	// Validate storage path using centralized validation
+	if config.StoragePath != "" {
+		if err := ValidateStoragePath(config.StoragePath); err != nil {
+			return err
+		}
+	}
+
+	// Validate S3 configuration if storage type is S3
+	if config.StorageType == model.StorageTypeS3 {
+		if err := ValidateS3Config(config); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ValidateS3Config validates S3 storage configuration completeness.
+// This function ensures all required S3 fields are provided when S3 storage is selected.
+//
+// Parameters:
+//   - config: Auto backup configuration with S3 settings
+//
+// Returns:
+//   - error: CosyError if S3 configuration is incomplete, nil if valid
+func ValidateS3Config(config *model.AutoBackup) error {
+	var missingFields []string
+
+	// Check required S3 fields
+	if config.S3Bucket == "" {
+		missingFields = append(missingFields, "bucket")
+	}
+	if config.S3AccessKeyID == "" {
+		missingFields = append(missingFields, "access_key_id")
+	}
+	if config.S3SecretAccessKey == "" {
+		missingFields = append(missingFields, "secret_access_key")
+	}
+
+	// Return error if any required fields are missing
+	if len(missingFields) > 0 {
+		return cosy.WrapErrorWithParams(ErrAutoBackupS3ConfigIncomplete, strings.Join(missingFields, ", "))
+	}
+
+	return nil
+}
+
+// RestoreAutoBackup restores a soft-deleted auto backup configuration.
+// This function restores the backup configuration and re-registers the cron job if enabled.
+//
+// Parameters:
+//   - id: Auto backup configuration ID to restore
+//
+// Returns:
+//   - error: Database error if restore fails
+func RestoreAutoBackup(id uint64) error {
+	// Restore the soft-deleted record
+	_, err := query.AutoBackup.Unscoped().Where(query.AutoBackup.ID.Eq(id)).Update(query.AutoBackup.DeletedAt, nil)
+	if err != nil {
+		return err
+	}
+
+	// Get the restored backup configuration
+	autoBackup, err := GetAutoBackupByID(id)
+	if err != nil {
+		return err
+	}
+
+	// Re-register cron job if the backup is enabled
+	if autoBackup.Enabled {
+		// Import cron package to register the job
+		// Note: This would require importing the cron package, which might create circular dependency
+		// The actual implementation should be handled at the API level
+		logger.Infof("Auto backup %d restored and needs cron job registration", id)
+	}
+
+	return nil
+}

+ 54 - 38
internal/backup/backup.go

@@ -13,40 +13,55 @@ import (
 	"github.com/uozi-tech/cosy/logger"
 )
 
-// Directory and file names
+// Constants for backup directory and file naming conventions
 const (
-	BackupDirPrefix = "nginx-ui-backup-"
-	NginxUIDir      = "nginx-ui"
-	NginxDir        = "nginx"
-	HashInfoFile    = "hash_info.txt"
-	NginxUIZipName  = "nginx-ui.zip"
-	NginxZipName    = "nginx.zip"
+	BackupDirPrefix = "nginx-ui-backup-" // Prefix for temporary backup directories
+	NginxUIDir      = "nginx-ui"         // Directory name for Nginx UI files in backup
+	NginxDir        = "nginx"            // Directory name for Nginx config files in backup
+	HashInfoFile    = "hash_info.txt"    // Filename for hash verification information
+	NginxUIZipName  = "nginx-ui.zip"     // Filename for Nginx UI archive within backup
+	NginxZipName    = "nginx.zip"        // Filename for Nginx config archive within backup
 )
 
-// BackupResult contains the results of a backup operation
+// BackupResult contains the complete results of a backup operation.
+// This structure encapsulates all data needed to restore or verify a backup.
 type BackupResult struct {
-	BackupContent []byte `json:"-"`       // Backup content as byte array
-	BackupName    string `json:"name"`    // Backup file name
-	AESKey        string `json:"aes_key"` // Base64 encoded AES key
-	AESIv         string `json:"aes_iv"`  // Base64 encoded AES IV
+	BackupContent []byte `json:"-"`       // Encrypted backup content as byte array (excluded from JSON)
+	BackupName    string `json:"name"`    // Generated backup filename with timestamp
+	AESKey        string `json:"aes_key"` // Base64 encoded AES encryption key
+	AESIv         string `json:"aes_iv"`  // Base64 encoded AES initialization vector
 }
 
-// HashInfo contains hash information for verification
+// HashInfo contains cryptographic hash information for backup verification.
+// This structure ensures backup integrity and provides metadata for restoration.
 type HashInfo struct {
-	NginxUIHash string `json:"nginx_ui_hash"`
-	NginxHash   string `json:"nginx_hash"`
-	Timestamp   string `json:"timestamp"`
-	Version     string `json:"version"`
+	NginxUIHash string `json:"nginx_ui_hash"` // SHA-256 hash of Nginx UI files archive
+	NginxHash   string `json:"nginx_hash"`    // SHA-256 hash of Nginx config files archive
+	Timestamp   string `json:"timestamp"`     // Backup creation timestamp
+	Version     string `json:"version"`       // Nginx UI version at backup time
 }
 
-// Backup creates a backup of nginx-ui configuration and database files,
-// and nginx configuration directory, compressed into an encrypted archive
+// Backup creates a comprehensive backup of nginx-ui configuration, database files,
+// and nginx configuration directory. The backup is compressed and encrypted for security.
+//
+// The backup process includes:
+//  1. Creating temporary directories for staging files
+//  2. Copying Nginx UI configuration and database files
+//  3. Copying Nginx configuration directory
+//  4. Creating individual ZIP archives for each component
+//  5. Calculating cryptographic hashes for integrity verification
+//  6. Encrypting all components with AES encryption
+//  7. Creating final encrypted archive in memory
+//
+// Returns:
+//   - BackupResult: Complete backup data including encrypted content and keys
+//   - error: CosyError if any step of the backup process fails
 func Backup() (BackupResult, error) {
-	// Generate timestamps for filenames
+	// Generate timestamp for unique backup identification
 	timestamp := time.Now().Format("20060102-150405")
 	backupName := fmt.Sprintf("backup-%s.zip", timestamp)
 
-	// Generate AES key and IV
+	// Generate cryptographic keys for AES encryption
 	key, err := GenerateAESKey()
 	if err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrGenerateAESKey, err.Error())
@@ -57,14 +72,14 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrGenerateIV, err.Error())
 	}
 
-	// Create temporary directory for files to be archived
+	// Create temporary directory for staging backup files
 	tempDir, err := os.MkdirTemp("", "nginx-ui-backup-*")
 	if err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateTempDir, err.Error())
 	}
-	defer os.RemoveAll(tempDir)
+	defer os.RemoveAll(tempDir) // Ensure cleanup of temporary files
 
-	// Create directories in temp
+	// Create subdirectories for organizing backup components
 	nginxUITempDir := filepath.Join(tempDir, NginxUIDir)
 	nginxTempDir := filepath.Join(tempDir, NginxDir)
 	if err := os.MkdirAll(nginxUITempDir, 0755); err != nil {
@@ -74,30 +89,31 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateTempSubDir, err.Error())
 	}
 
-	// Backup nginx-ui config and database to a directory
+	// Stage Nginx UI configuration and database files
 	if err := backupNginxUIFiles(nginxUITempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrBackupNginxUI, err.Error())
 	}
 
-	// Backup nginx configs to a directory
+	// Stage Nginx configuration files
 	if err := backupNginxFiles(nginxTempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrBackupNginx, err.Error())
 	}
 
-	// Create individual zip files for nginx-ui and nginx directories
+	// Create individual ZIP archives for each component
 	nginxUIZipPath := filepath.Join(tempDir, NginxUIZipName)
 	nginxZipPath := filepath.Join(tempDir, NginxZipName)
 
-	// Create zip archives for each directory
+	// Compress Nginx UI files into archive
 	if err := createZipArchive(nginxUIZipPath, nginxUITempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 	}
 
+	// Compress Nginx configuration files into archive
 	if err := createZipArchive(nginxZipPath, nginxTempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 	}
 
-	// Calculate hashes for the zip files
+	// Calculate cryptographic hashes for integrity verification
 	nginxUIHash, err := calculateFileHash(nginxUIZipPath)
 	if err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
@@ -108,10 +124,10 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
 	}
 
-	// Get current version information
+	// Gather version information for backup metadata
 	versionInfo := version.GetVersionInfo()
 
-	// Create hash info file
+	// Create hash verification file with metadata
 	hashInfo := HashInfo{
 		NginxUIHash: nginxUIHash,
 		NginxHash:   nginxHash,
@@ -119,13 +135,13 @@ func Backup() (BackupResult, error) {
 		Version:     versionInfo.Version,
 	}
 
-	// Write hash info to file
+	// Write hash information to verification file
 	hashInfoPath := filepath.Join(tempDir, HashInfoFile)
 	if err := writeHashInfoFile(hashInfoPath, hashInfo); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateHashFile, err.Error())
 	}
 
-	// Encrypt the individual files
+	// Encrypt all backup components for security
 	if err := encryptFile(hashInfoPath, key, iv); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrEncryptFile, HashInfoFile)
 	}
@@ -138,7 +154,7 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrEncryptNginxDir, err.Error())
 	}
 
-	// Remove the original directories to avoid duplicating them in the final archive
+	// Clean up unencrypted directories to prevent duplication in final archive
 	if err := os.RemoveAll(nginxUITempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCleanupTempDir, err.Error())
 	}
@@ -146,17 +162,17 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCleanupTempDir, err.Error())
 	}
 
-	// Create final zip file to memory buffer
+	// Create final encrypted backup archive in memory
 	var buffer bytes.Buffer
 	if err := createZipArchiveToBuffer(&buffer, tempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 	}
 
-	// Convert AES key and IV to base64 encoded strings
+	// Encode encryption keys as base64 for safe transmission/storage
 	keyBase64 := base64.StdEncoding.EncodeToString(key)
 	ivBase64 := base64.StdEncoding.EncodeToString(iv)
 
-	// Return result
+	// Assemble final backup result
 	result := BackupResult{
 		BackupContent: buffer.Bytes(),
 		BackupName:    backupName,
@@ -164,6 +180,6 @@ func Backup() (BackupResult, error) {
 		AESIv:         ivBase64,
 	}
 
-	logger.Infof("Backup created successfully: %s", backupName)
+	logger.Infof("Backup created successfully: %s (size: %d bytes)", backupName, len(buffer.Bytes()))
 	return result, nil
 }

+ 24 - 0
internal/backup/errors.go

@@ -80,4 +80,28 @@ var (
 	ErrCalculateUIHash    = errScope.New(4802, "Failed to calculate Nginx UI hash: {0}")
 	ErrCalculateNginxHash = errScope.New(4803, "Failed to calculate Nginx hash: {0}")
 	ErrHashMismatch       = errScope.New(4804, "Hash verification failed: file integrity compromised")
+
+	// Auto backup errors
+	ErrAutoBackupPathNotAllowed        = errScope.New(4901, "Backup path not in granted access paths: {0}")
+	ErrAutoBackupStoragePathNotAllowed = errScope.New(4902, "Storage path not in granted access paths: {0}")
+	ErrAutoBackupPathRequired          = errScope.New(4903, "Backup path is required for custom directory backup")
+	ErrAutoBackupS3ConfigIncomplete    = errScope.New(4904, "S3 configuration is incomplete: missing {0}")
+	ErrAutoBackupUnsupportedType       = errScope.New(4905, "Unsupported backup type: {0}")
+	ErrAutoBackupCreateDir             = errScope.New(4906, "Failed to create backup directory: {0}")
+	ErrAutoBackupWriteFile             = errScope.New(4907, "Failed to write backup file: {0}")
+	ErrAutoBackupWriteKeyFile          = errScope.New(4908, "Failed to write security key file: {0}")
+	ErrAutoBackupS3Upload              = errScope.New(4909, "S3 upload failed: {0}")
+	ErrAutoBackupS3Connection          = errScope.New(4920, "S3 connection test failed: {0}")
+	ErrAutoBackupS3BucketAccess        = errScope.New(4921, "S3 bucket access denied: {0}")
+	ErrAutoBackupS3InvalidCredentials  = errScope.New(4922, "S3 credentials are invalid: {0}")
+	ErrAutoBackupS3InvalidEndpoint     = errScope.New(4923, "S3 endpoint is invalid: {0}")
+
+	// Path validation errors
+	ErrInvalidPath            = errScope.New(4910, "Invalid path: {0}")
+	ErrPathNotInGrantedAccess = errScope.New(4911, "Path not in granted access paths: {0}")
+	ErrBackupPathNotExist     = errScope.New(4912, "Backup path does not exist: {0}")
+	ErrBackupPathAccess       = errScope.New(4913, "Cannot access backup path {0}: {1}")
+	ErrBackupPathNotDirectory = errScope.New(4914, "Backup path is not a directory: {0}")
+	ErrCreateStorageDir       = errScope.New(4915, "Failed to create storage directory {0}: {1}")
+	ErrStoragePathAccess      = errScope.New(4916, "Cannot access storage path {0}: {1}")
 )

+ 241 - 0
internal/backup/s3_client.go

@@ -0,0 +1,241 @@
+package backup
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"time"
+
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/config"
+	"github.com/aws/aws-sdk-go-v2/credentials"
+	"github.com/aws/aws-sdk-go-v2/service/s3"
+	"github.com/uozi-tech/cosy"
+	"github.com/uozi-tech/cosy/logger"
+)
+
+// S3Client wraps the AWS S3 client with backup-specific functionality
+type S3Client struct {
+	client *s3.Client
+	bucket string
+}
+
+// NewS3Client creates a new S3 client from auto backup configuration.
+// This function initializes the AWS S3 client with the provided credentials and configuration.
+//
+// Parameters:
+//   - autoBackup: The auto backup configuration containing S3 settings
+//
+// Returns:
+//   - *S3Client: Configured S3 client wrapper
+//   - error: CosyError if client creation fails
+func NewS3Client(autoBackup *model.AutoBackup) (*S3Client, error) {
+	// Create AWS configuration with static credentials
+	cfg, err := config.LoadDefaultConfig(context.TODO(),
+		config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
+			autoBackup.S3AccessKeyID,
+			autoBackup.S3SecretAccessKey,
+			"", // session token (not used for static credentials)
+		)),
+		config.WithRegion(getS3Region(autoBackup.S3Region)),
+	)
+	if err != nil {
+		return nil, cosy.WrapErrorWithParams(ErrAutoBackupS3Upload, fmt.Sprintf("failed to load AWS config: %v", err))
+	}
+
+	// Create S3 client with custom endpoint if provided
+	var s3Client *s3.Client
+	if autoBackup.S3Endpoint != "" {
+		s3Client = s3.NewFromConfig(cfg, func(o *s3.Options) {
+			o.BaseEndpoint = aws.String(autoBackup.S3Endpoint)
+			o.UsePathStyle = true // Use path-style addressing for custom endpoints
+		})
+	} else {
+		s3Client = s3.NewFromConfig(cfg)
+	}
+
+	return &S3Client{
+		client: s3Client,
+		bucket: autoBackup.S3Bucket,
+	}, nil
+}
+
+// UploadFile uploads a file to S3 with the specified key.
+// This function handles the actual upload operation with proper error handling and logging.
+//
+// Parameters:
+//   - ctx: Context for the upload operation
+//   - key: S3 object key (path) for the uploaded file
+//   - data: File content to upload
+//   - contentType: MIME type of the file content
+//
+// Returns:
+//   - error: CosyError if upload fails
+func (s3c *S3Client) UploadFile(ctx context.Context, key string, data []byte, contentType string) error {
+	logger.Infof("Uploading file to S3: bucket=%s, key=%s, size=%d bytes", s3c.bucket, key, len(data))
+
+	// Create upload input
+	input := &s3.PutObjectInput{
+		Bucket:      aws.String(s3c.bucket),
+		Key:         aws.String(key),
+		Body:        bytes.NewReader(data),
+		ContentType: aws.String(contentType),
+		Metadata: map[string]string{
+			"uploaded-by":    "nginx-ui",
+			"upload-time":    time.Now().UTC().Format(time.RFC3339),
+			"content-length": fmt.Sprintf("%d", len(data)),
+		},
+	}
+
+	// Perform the upload
+	_, err := s3c.client.PutObject(ctx, input)
+	if err != nil {
+		return cosy.WrapErrorWithParams(ErrAutoBackupS3Upload, fmt.Sprintf("failed to upload to S3: %v", err))
+	}
+
+	logger.Infof("Successfully uploaded file to S3: bucket=%s, key=%s", s3c.bucket, key)
+	return nil
+}
+
+// UploadBackupFiles uploads backup files to S3 with proper naming and organization.
+// This function handles uploading both the backup file and optional key file.
+//
+// Parameters:
+//   - ctx: Context for the upload operations
+//   - result: Backup execution result containing file paths
+//   - autoBackup: Auto backup configuration for S3 path construction
+//
+// Returns:
+//   - error: CosyError if any upload fails
+func (s3c *S3Client) UploadBackupFiles(ctx context.Context, result *BackupExecutionResult, autoBackup *model.AutoBackup) error {
+	// Read backup file content
+	backupData, err := readFileContent(result.FilePath)
+	if err != nil {
+		return cosy.WrapErrorWithParams(ErrAutoBackupS3Upload, fmt.Sprintf("failed to read backup file: %v", err))
+	}
+
+	// Construct S3 key for backup file
+	backupFileName := filepath.Base(result.FilePath)
+	backupKey := constructS3Key(autoBackup.StoragePath, backupFileName)
+
+	// Upload backup file
+	if err := s3c.UploadFile(ctx, backupKey, backupData, "application/zip"); err != nil {
+		return err
+	}
+
+	// Upload key file if it exists (for encrypted backups)
+	if result.KeyPath != "" {
+		keyData, err := readFileContent(result.KeyPath)
+		if err != nil {
+			return cosy.WrapErrorWithParams(ErrAutoBackupS3Upload, fmt.Sprintf("failed to read key file: %v", err))
+		}
+
+		keyFileName := filepath.Base(result.KeyPath)
+		keyKey := constructS3Key(autoBackup.StoragePath, keyFileName)
+
+		if err := s3c.UploadFile(ctx, keyKey, keyData, "text/plain"); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// TestS3Connection tests the S3 connection and permissions.
+// This function verifies that the S3 configuration is valid and accessible.
+//
+// Parameters:
+//   - ctx: Context for the test operation
+//
+// Returns:
+//   - error: CosyError if connection test fails
+func (s3c *S3Client) TestS3Connection(ctx context.Context) error {
+	logger.Infof("Testing S3 connection: bucket=%s", s3c.bucket)
+
+	// Try to head the bucket to verify access
+	_, err := s3c.client.HeadBucket(ctx, &s3.HeadBucketInput{
+		Bucket: aws.String(s3c.bucket),
+	})
+	if err != nil {
+		return cosy.WrapErrorWithParams(ErrAutoBackupS3Upload, fmt.Sprintf("S3 connection test failed: %v", err))
+	}
+
+	logger.Infof("S3 connection test successful: bucket=%s", s3c.bucket)
+	return nil
+}
+
+// getS3Region returns the S3 region, defaulting to us-east-1 if not specified.
+// This function ensures a valid region is always provided to the AWS SDK.
+//
+// Parameters:
+//   - region: The configured S3 region
+//
+// Returns:
+//   - string: Valid AWS region string
+func getS3Region(region string) string {
+	if region == "" {
+		return "us-east-1" // Default region
+	}
+	return region
+}
+
+// constructS3Key constructs a proper S3 object key from storage path and filename.
+// This function ensures consistent S3 key formatting across the application.
+//
+// Parameters:
+//   - storagePath: Base storage path in S3
+//   - filename: Name of the file
+//
+// Returns:
+//   - string: Properly formatted S3 object key
+func constructS3Key(storagePath, filename string) string {
+	// Ensure storage path doesn't start with slash and ends with slash
+	if storagePath == "" {
+		return filename
+	}
+
+	// Remove leading slash if present
+	if storagePath[0] == '/' {
+		storagePath = storagePath[1:]
+	}
+
+	// Add trailing slash if not present
+	if storagePath[len(storagePath)-1] != '/' {
+		storagePath += "/"
+	}
+
+	return storagePath + filename
+}
+
+// readFileContent reads the entire content of a file into memory.
+// This function provides a centralized way to read file content for S3 uploads.
+//
+// Parameters:
+//   - filePath: Path to the file to read
+//
+// Returns:
+//   - []byte: File content
+//   - error: Standard error if file reading fails
+func readFileContent(filePath string) ([]byte, error) {
+	return os.ReadFile(filePath)
+}
+
+// TestS3ConnectionForConfig tests S3 connection for a given auto backup configuration.
+// This function is used by the API to validate S3 settings before saving.
+//
+// Parameters:
+//   - autoBackup: Auto backup configuration with S3 settings
+//
+// Returns:
+//   - error: CosyError if connection test fails
+func TestS3ConnectionForConfig(autoBackup *model.AutoBackup) error {
+	s3Client, err := NewS3Client(autoBackup)
+	if err != nil {
+		return err
+	}
+
+	return s3Client.TestS3Connection(context.Background())
+}

+ 153 - 0
internal/backup/s3_client_test.go

@@ -0,0 +1,153 @@
+package backup
+
+import (
+	"testing"
+
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestConstructS3Key(t *testing.T) {
+	tests := []struct {
+		name        string
+		storagePath string
+		filename    string
+		expected    string
+	}{
+		{
+			name:        "empty storage path",
+			storagePath: "",
+			filename:    "backup.zip",
+			expected:    "backup.zip",
+		},
+		{
+			name:        "storage path with trailing slash",
+			storagePath: "backups/",
+			filename:    "backup.zip",
+			expected:    "backups/backup.zip",
+		},
+		{
+			name:        "storage path without trailing slash",
+			storagePath: "backups",
+			filename:    "backup.zip",
+			expected:    "backups/backup.zip",
+		},
+		{
+			name:        "storage path with leading slash",
+			storagePath: "/backups",
+			filename:    "backup.zip",
+			expected:    "backups/backup.zip",
+		},
+		{
+			name:        "storage path with both leading and trailing slash",
+			storagePath: "/backups/",
+			filename:    "backup.zip",
+			expected:    "backups/backup.zip",
+		},
+		{
+			name:        "nested storage path",
+			storagePath: "nginx-ui/backups",
+			filename:    "backup.zip",
+			expected:    "nginx-ui/backups/backup.zip",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := constructS3Key(tt.storagePath, tt.filename)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}
+
+func TestGetS3Region(t *testing.T) {
+	tests := []struct {
+		name     string
+		region   string
+		expected string
+	}{
+		{
+			name:     "empty region",
+			region:   "",
+			expected: "us-east-1",
+		},
+		{
+			name:     "valid region",
+			region:   "eu-west-1",
+			expected: "eu-west-1",
+		},
+		{
+			name:     "us-west-2 region",
+			region:   "us-west-2",
+			expected: "us-west-2",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := getS3Region(tt.region)
+			assert.Equal(t, tt.expected, result)
+		})
+	}
+}
+
+func TestNewS3Client_ValidationErrors(t *testing.T) {
+	tests := []struct {
+		name        string
+		autoBackup  *model.AutoBackup
+		expectError bool
+	}{
+		{
+			name: "valid configuration",
+			autoBackup: &model.AutoBackup{
+				S3AccessKeyID:     "test-access-key",
+				S3SecretAccessKey: "test-secret-key",
+				S3Bucket:          "test-bucket",
+				S3Region:          "us-east-1",
+			},
+			expectError: false,
+		},
+		{
+			name: "valid configuration with custom endpoint",
+			autoBackup: &model.AutoBackup{
+				S3AccessKeyID:     "test-access-key",
+				S3SecretAccessKey: "test-secret-key",
+				S3Bucket:          "test-bucket",
+				S3Region:          "us-east-1",
+				S3Endpoint:        "https://s3.example.com",
+			},
+			expectError: false,
+		},
+		{
+			name: "empty region defaults to us-east-1",
+			autoBackup: &model.AutoBackup{
+				S3AccessKeyID:     "test-access-key",
+				S3SecretAccessKey: "test-secret-key",
+				S3Bucket:          "test-bucket",
+				S3Region:          "",
+			},
+			expectError: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			client, err := NewS3Client(tt.autoBackup)
+
+			if tt.expectError {
+				assert.Error(t, err)
+				assert.Nil(t, client)
+			} else {
+				// Note: This will fail in CI/test environment without AWS credentials
+				// but the client creation itself should succeed
+				if err != nil {
+					// Allow AWS credential errors in test environment
+					assert.Contains(t, err.Error(), "failed to load AWS config")
+				} else {
+					assert.NotNil(t, client)
+					assert.Equal(t, tt.autoBackup.S3Bucket, client.bucket)
+				}
+			}
+		})
+	}
+}

+ 126 - 15
internal/backup/utils.go

@@ -4,34 +4,147 @@ import (
 	"io"
 	"os"
 	"path/filepath"
+	"strings"
 
+	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/uozi-tech/cosy"
 )
 
-// copyFile copies a file from src to dst
+// ValidatePathAccess validates if a given path is within the granted access paths.
+// This function ensures that all backup read/write operations are restricted to
+// authorized directories only, preventing unauthorized file system access.
+//
+// Parameters:
+//   - path: The file system path to validate
+//
+// Returns:
+//   - error: CosyError if path is not allowed, nil if path is valid
+func ValidatePathAccess(path string) error {
+	if path == "" {
+		return cosy.WrapErrorWithParams(ErrInvalidPath, "path cannot be empty")
+	}
+
+	// Clean the path to resolve any relative components like ".." or "."
+	cleanPath := filepath.Clean(path)
+
+	// Check if the path is within any of the granted access paths
+	for _, allowedPath := range settings.BackupSettings.GrantedAccessPath {
+		if allowedPath == "" {
+			continue
+		}
+
+		// Clean the allowed path as well for consistent comparison
+		cleanAllowedPath := filepath.Clean(allowedPath)
+
+		// Check if the path is within the allowed path
+		if strings.HasPrefix(cleanPath, cleanAllowedPath) {
+			// Ensure it's actually a subdirectory or the same directory
+			// This prevents "/tmp" from matching "/tmpfoo"
+			if cleanPath == cleanAllowedPath || strings.HasPrefix(cleanPath, cleanAllowedPath+string(filepath.Separator)) {
+				return nil
+			}
+		}
+	}
+
+	return cosy.WrapErrorWithParams(ErrPathNotInGrantedAccess, cleanPath)
+}
+
+// ValidateBackupPath validates the backup source path for custom directory backups.
+// This function checks if the source directory exists and is accessible.
+//
+// Parameters:
+//   - path: The backup source path to validate
+//
+// Returns:
+//   - error: CosyError if validation fails, nil if path is valid
+func ValidateBackupPath(path string) error {
+	// First check if path is in granted access paths
+	if err := ValidatePathAccess(path); err != nil {
+		return err
+	}
+
+	// Check if the path exists and is a directory
+	info, err := os.Stat(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return cosy.WrapErrorWithParams(ErrBackupPathNotExist, path)
+		}
+		return cosy.WrapErrorWithParams(ErrBackupPathAccess, path, err.Error())
+	}
+
+	if !info.IsDir() {
+		return cosy.WrapErrorWithParams(ErrBackupPathNotDirectory, path)
+	}
+
+	return nil
+}
+
+// ValidateStoragePath validates the storage destination path for backup files.
+// This function ensures the storage directory exists or can be created.
+//
+// Parameters:
+//   - path: The storage destination path to validate
+//
+// Returns:
+//   - error: CosyError if validation fails, nil if path is valid
+func ValidateStoragePath(path string) error {
+	// First check if path is in granted access paths
+	if err := ValidatePathAccess(path); err != nil {
+		return err
+	}
+
+	// Check if the directory exists, if not try to create it
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		if err := os.MkdirAll(path, 0755); err != nil {
+			return cosy.WrapErrorWithParams(ErrCreateStorageDir, path, err.Error())
+		}
+	} else if err != nil {
+		return cosy.WrapErrorWithParams(ErrStoragePathAccess, path, err.Error())
+	}
+
+	return nil
+}
+
+// copyFile copies a file from source to destination with proper error handling.
+// This function handles file copying operations used in backup processes.
+//
+// Parameters:
+//   - src: Source file path
+//   - dst: Destination file path
+//
+// Returns:
+//   - error: Standard error if copy operation fails
 func copyFile(src, dst string) error {
-	// Open source file
+	// Open source file for reading
 	source, err := os.Open(src)
 	if err != nil {
 		return err
 	}
 	defer source.Close()
 
-	// Create destination file
+	// Create destination file for writing
 	destination, err := os.Create(dst)
 	if err != nil {
 		return err
 	}
 	defer destination.Close()
 
-	// Copy content
+	// Copy file content from source to destination
 	_, err = io.Copy(destination, source)
 	return err
 }
 
-// copyDirectory copies a directory recursively from src to dst
+// copyDirectory recursively copies a directory from source to destination.
+// This function preserves file permissions and handles symbolic links properly.
+//
+// Parameters:
+//   - src: Source directory path
+//   - dst: Destination directory path
+//
+// Returns:
+//   - error: CosyError if copy operation fails
 func copyDirectory(src, dst string) error {
-	// Check if source is a directory
+	// Verify source is a directory
 	srcInfo, err := os.Stat(src)
 	if err != nil {
 		return err
@@ -40,18 +153,18 @@ func copyDirectory(src, dst string) error {
 		return cosy.WrapErrorWithParams(ErrCopyNginxConfigDir, "%s is not a directory", src)
 	}
 
-	// Create destination directory
+	// Create destination directory with same permissions as source
 	if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil {
 		return err
 	}
 
-	// Walk through source directory
+	// Walk through source directory and copy all contents
 	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
 		if err != nil {
 			return err
 		}
 
-		// Calculate relative path
+		// Calculate relative path from source root
 		relPath, err := filepath.Rel(src, path)
 		if err != nil {
 			return err
@@ -60,26 +173,24 @@ func copyDirectory(src, dst string) error {
 			return nil
 		}
 
-		// Create target path
+		// Construct target path
 		targetPath := filepath.Join(dst, relPath)
 
-		// Check if it's a symlink
+		// Handle symbolic links by recreating them
 		if info.Mode()&os.ModeSymlink != 0 {
-			// Read the link
 			linkTarget, err := os.Readlink(path)
 			if err != nil {
 				return err
 			}
-			// Create symlink at target path
 			return os.Symlink(linkTarget, targetPath)
 		}
 
-		// If it's a directory, create it
+		// Create directories with original permissions
 		if info.IsDir() {
 			return os.MkdirAll(targetPath, info.Mode())
 		}
 
-		// If it's a file, copy it
+		// Copy regular files
 		return copyFile(path, targetPath)
 	})
 }

+ 146 - 0
internal/cron/auto_backup.go

@@ -0,0 +1,146 @@
+package cron
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/0xJacky/Nginx-UI/internal/backup"
+	"github.com/go-co-op/gocron/v2"
+	"github.com/uozi-tech/cosy/logger"
+)
+
+var (
+	autoBackupJobs = make(map[uint64]gocron.Job)
+	autoBackupMu   sync.RWMutex
+)
+
+// setupAutoBackupJobs initializes all auto backup jobs from database
+func setupAutoBackupJobs(s gocron.Scheduler) error {
+	autoBackups, err := backup.GetEnabledAutoBackups()
+	if err != nil {
+		return fmt.Errorf("failed to get enabled auto backups: %w", err)
+	}
+
+	for _, autoBackup := range autoBackups {
+		err := addAutoBackupJob(s, autoBackup.ID, autoBackup.CronExpression)
+		if err != nil {
+			logger.Errorf("Failed to add auto backup job for %s: %v", autoBackup.Name, err)
+		}
+	}
+
+	return nil
+}
+
+// addAutoBackupJob adds a single auto backup job to the scheduler
+func addAutoBackupJob(s gocron.Scheduler, backupID uint64, cronExpression string) error {
+	autoBackupMu.Lock()
+	defer autoBackupMu.Unlock()
+
+	// Remove existing job if it exists
+	if existingJob, exists := autoBackupJobs[backupID]; exists {
+		err := s.RemoveJob(existingJob.ID())
+		if err != nil {
+			logger.Errorf("Failed to remove existing auto backup job %d: %v", backupID, err)
+		}
+		delete(autoBackupJobs, backupID)
+	}
+
+	// Create new job
+	job, err := s.NewJob(
+		gocron.CronJob(cronExpression, false),
+		gocron.NewTask(executeAutoBackupTask, backupID),
+		gocron.WithName(fmt.Sprintf("auto_backup_%d", backupID)),
+	)
+	if err != nil {
+		return fmt.Errorf("failed to create auto backup job: %w", err)
+	}
+
+	autoBackupJobs[backupID] = job
+	logger.Infof("Added auto backup job %d with cron expression: %s", backupID, cronExpression)
+	return nil
+}
+
+// removeAutoBackupJob removes an auto backup job from the scheduler
+func removeAutoBackupJob(s gocron.Scheduler, backupID uint64) error {
+	autoBackupMu.Lock()
+	defer autoBackupMu.Unlock()
+
+	if job, exists := autoBackupJobs[backupID]; exists {
+		err := s.RemoveJob(job.ID())
+		if err != nil {
+			return fmt.Errorf("failed to remove auto backup job: %w", err)
+		}
+		delete(autoBackupJobs, backupID)
+		logger.Infof("Removed auto backup job %d", backupID)
+	}
+
+	return nil
+}
+
+// executeAutoBackupTask executes a single auto backup task
+func executeAutoBackupTask(backupID uint64) {
+	logger.Infof("Executing auto backup task %d", backupID)
+
+	// Get backup configuration
+	autoBackup, err := backup.GetAutoBackupByID(backupID)
+	if err != nil {
+		logger.Errorf("Failed to get auto backup configuration %d: %v", backupID, err)
+		return
+	}
+
+	// Check if backup is still enabled
+	if !autoBackup.Enabled {
+		removeAutoBackupJob(s, backupID)
+		logger.Infof("Auto backup %d is disabled, skipping execution", backupID)
+		return
+	}
+
+	// Execute backup
+	err = backup.ExecuteAutoBackup(autoBackup)
+	if err != nil {
+		logger.Errorf("Auto backup task %d failed: %v", backupID, err)
+	} else {
+		logger.Infof("Auto backup task %d completed successfully", backupID)
+	}
+}
+
+// RestartAutoBackupJobs restarts all auto backup jobs
+func RestartAutoBackupJobs() error {
+	logger.Info("Restarting auto backup jobs...")
+
+	autoBackupMu.Lock()
+	defer autoBackupMu.Unlock()
+
+	// Remove all existing jobs
+	for backupID, job := range autoBackupJobs {
+		err := s.RemoveJob(job.ID())
+		if err != nil {
+			logger.Errorf("Failed to remove auto backup job %d: %v", backupID, err)
+		}
+	}
+	autoBackupJobs = make(map[uint64]gocron.Job)
+
+	// Re-add all enabled jobs
+	err := setupAutoBackupJobs(s)
+	if err != nil {
+		return fmt.Errorf("failed to restart auto backup jobs: %w", err)
+	}
+
+	logger.Info("Auto backup jobs restarted successfully")
+	return nil
+}
+
+// AddAutoBackupJob adds or updates an auto backup job (public API)
+func AddAutoBackupJob(backupID uint64, cronExpression string) error {
+	return addAutoBackupJob(s, backupID, cronExpression)
+}
+
+// RemoveAutoBackupJob removes an auto backup job (public API)
+func RemoveAutoBackupJob(backupID uint64) error {
+	return removeAutoBackupJob(s, backupID)
+}
+
+// UpdateAutoBackupJob updates an auto backup job (public API)
+func UpdateAutoBackupJob(backupID uint64, cronExpression string) error {
+	return addAutoBackupJob(s, backupID, cronExpression)
+}

+ 6 - 0
internal/cron/cron.go

@@ -41,6 +41,12 @@ func InitCronJobs(ctx context.Context) {
 		logger.Fatalf("CleanExpiredAuthToken Err: %v\n", err)
 	}
 
+	// Initialize auto backup jobs
+	err = setupAutoBackupJobs(s)
+	if err != nil {
+		logger.Fatalf("AutoBackup Err: %v\n", err)
+	}
+
 	// Start the scheduler
 	s.Start()
 

+ 54 - 0
model/auto_backup.go

@@ -0,0 +1,54 @@
+package model
+
+import (
+	"time"
+)
+
+// BackupType represents the type of backup
+type BackupType string
+
+const (
+	BackupTypeNginxConfig   BackupType = "nginx_config"
+	BackupTypeNginxUIConfig BackupType = "nginx_ui_config"
+	BackupTypeBothConfig    BackupType = "both_config"
+	BackupTypeCustomDir     BackupType = "custom_dir"
+)
+
+// StorageType represents where the backup is stored
+type StorageType string
+
+const (
+	StorageTypeLocal StorageType = "local"
+	StorageTypeS3    StorageType = "s3"
+)
+
+// BackupStatus represents the status of the last backup
+type BackupStatus string
+
+const (
+	BackupStatusPending BackupStatus = "pending"
+	BackupStatusSuccess BackupStatus = "success"
+	BackupStatusFailed  BackupStatus = "failed"
+)
+
+// AutoBackup represents an automatic backup configuration
+type AutoBackup struct {
+	Model
+	Name             string       `json:"name" gorm:"not null;comment:Backup task name"`
+	BackupType       BackupType   `json:"backup_type" gorm:"index;not null;comment:Type of backup"`
+	StorageType      StorageType  `json:"storage_type" gorm:"index;not null;comment:Storage type (local/s3)"`
+	BackupPath       string       `json:"backup_path" gorm:"comment:Custom directory path for backup"`
+	StoragePath      string       `json:"storage_path" gorm:"not null;comment:Storage destination path"`
+	CronExpression   string       `json:"cron_expression" gorm:"not null;comment:Cron expression for scheduling"`
+	Enabled          bool         `json:"enabled" gorm:"index;default:true;comment:Whether the backup task is enabled"`
+	LastBackupTime   *time.Time   `json:"last_backup_time" gorm:"comment:Last backup execution time"`
+	LastBackupStatus BackupStatus `json:"last_backup_status" gorm:"default:'pending';comment:Status of last backup"`
+	LastBackupError  string       `json:"last_backup_error" gorm:"comment:Error message from last backup if failed"`
+
+	// S3 Configuration (only used when StorageType is S3)
+	S3Endpoint        string `json:"s3_endpoint" gorm:"comment:S3 endpoint URL"`
+	S3AccessKeyID     string `json:"s3_access_key_id" gorm:"comment:S3 access key ID"`
+	S3SecretAccessKey string `json:"s3_secret_access_key" gorm:"comment:S3 secret access key"`
+	S3Bucket          string `json:"s3_bucket" gorm:"comment:S3 bucket name"`
+	S3Region          string `json:"s3_region" gorm:"comment:S3 region"`
+}

+ 1 - 0
model/model.go

@@ -34,6 +34,7 @@ func GenerateAllModel() []any {
 		Passkey{},
 		EnvGroup{},
 		ExternalNotify{},
+		AutoBackup{},
 	}
 }
 

+ 422 - 0
query/auto_backups.gen.go

@@ -0,0 +1,422 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package query
+
+import (
+	"context"
+	"strings"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"gorm.io/gorm/schema"
+
+	"gorm.io/gen"
+	"gorm.io/gen/field"
+
+	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
+)
+
+func newAutoBackup(db *gorm.DB, opts ...gen.DOOption) autoBackup {
+	_autoBackup := autoBackup{}
+
+	_autoBackup.autoBackupDo.UseDB(db, opts...)
+	_autoBackup.autoBackupDo.UseModel(&model.AutoBackup{})
+
+	tableName := _autoBackup.autoBackupDo.TableName()
+	_autoBackup.ALL = field.NewAsterisk(tableName)
+	_autoBackup.ID = field.NewUint64(tableName, "id")
+	_autoBackup.CreatedAt = field.NewTime(tableName, "created_at")
+	_autoBackup.UpdatedAt = field.NewTime(tableName, "updated_at")
+	_autoBackup.DeletedAt = field.NewField(tableName, "deleted_at")
+	_autoBackup.Name = field.NewString(tableName, "name")
+	_autoBackup.BackupType = field.NewString(tableName, "backup_type")
+	_autoBackup.StorageType = field.NewString(tableName, "storage_type")
+	_autoBackup.BackupPath = field.NewString(tableName, "backup_path")
+	_autoBackup.StoragePath = field.NewString(tableName, "storage_path")
+	_autoBackup.CronExpression = field.NewString(tableName, "cron_expression")
+	_autoBackup.Enabled = field.NewBool(tableName, "enabled")
+	_autoBackup.LastBackupTime = field.NewTime(tableName, "last_backup_time")
+	_autoBackup.LastBackupStatus = field.NewString(tableName, "last_backup_status")
+	_autoBackup.LastBackupError = field.NewString(tableName, "last_backup_error")
+	_autoBackup.S3Endpoint = field.NewString(tableName, "s3_endpoint")
+	_autoBackup.S3AccessKeyID = field.NewString(tableName, "s3_access_key_id")
+	_autoBackup.S3SecretAccessKey = field.NewString(tableName, "s3_secret_access_key")
+	_autoBackup.S3Bucket = field.NewString(tableName, "s3_bucket")
+	_autoBackup.S3Region = field.NewString(tableName, "s3_region")
+
+	_autoBackup.fillFieldMap()
+
+	return _autoBackup
+}
+
+type autoBackup struct {
+	autoBackupDo
+
+	ALL               field.Asterisk
+	ID                field.Uint64
+	CreatedAt         field.Time
+	UpdatedAt         field.Time
+	DeletedAt         field.Field
+	Name              field.String // Backup task name
+	BackupType        field.String // Type of backup
+	StorageType       field.String // Storage type (local/s3)
+	BackupPath        field.String // Custom directory path for backup
+	StoragePath       field.String // Storage destination path
+	CronExpression    field.String // Cron expression for scheduling
+	Enabled           field.Bool   // Whether the backup task is enabled
+	LastBackupTime    field.Time   // Last backup execution time
+	LastBackupStatus  field.String // Status of last backup
+	LastBackupError   field.String // Error message from last backup if failed
+	S3Endpoint        field.String // S3 endpoint URL
+	S3AccessKeyID     field.String // S3 access key ID
+	S3SecretAccessKey field.String // S3 secret access key
+	S3Bucket          field.String // S3 bucket name
+	S3Region          field.String // S3 region
+
+	fieldMap map[string]field.Expr
+}
+
+func (a autoBackup) Table(newTableName string) *autoBackup {
+	a.autoBackupDo.UseTable(newTableName)
+	return a.updateTableName(newTableName)
+}
+
+func (a autoBackup) As(alias string) *autoBackup {
+	a.autoBackupDo.DO = *(a.autoBackupDo.As(alias).(*gen.DO))
+	return a.updateTableName(alias)
+}
+
+func (a *autoBackup) updateTableName(table string) *autoBackup {
+	a.ALL = field.NewAsterisk(table)
+	a.ID = field.NewUint64(table, "id")
+	a.CreatedAt = field.NewTime(table, "created_at")
+	a.UpdatedAt = field.NewTime(table, "updated_at")
+	a.DeletedAt = field.NewField(table, "deleted_at")
+	a.Name = field.NewString(table, "name")
+	a.BackupType = field.NewString(table, "backup_type")
+	a.StorageType = field.NewString(table, "storage_type")
+	a.BackupPath = field.NewString(table, "backup_path")
+	a.StoragePath = field.NewString(table, "storage_path")
+	a.CronExpression = field.NewString(table, "cron_expression")
+	a.Enabled = field.NewBool(table, "enabled")
+	a.LastBackupTime = field.NewTime(table, "last_backup_time")
+	a.LastBackupStatus = field.NewString(table, "last_backup_status")
+	a.LastBackupError = field.NewString(table, "last_backup_error")
+	a.S3Endpoint = field.NewString(table, "s3_endpoint")
+	a.S3AccessKeyID = field.NewString(table, "s3_access_key_id")
+	a.S3SecretAccessKey = field.NewString(table, "s3_secret_access_key")
+	a.S3Bucket = field.NewString(table, "s3_bucket")
+	a.S3Region = field.NewString(table, "s3_region")
+
+	a.fillFieldMap()
+
+	return a
+}
+
+func (a *autoBackup) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
+	_f, ok := a.fieldMap[fieldName]
+	if !ok || _f == nil {
+		return nil, false
+	}
+	_oe, ok := _f.(field.OrderExpr)
+	return _oe, ok
+}
+
+func (a *autoBackup) fillFieldMap() {
+	a.fieldMap = make(map[string]field.Expr, 19)
+	a.fieldMap["id"] = a.ID
+	a.fieldMap["created_at"] = a.CreatedAt
+	a.fieldMap["updated_at"] = a.UpdatedAt
+	a.fieldMap["deleted_at"] = a.DeletedAt
+	a.fieldMap["name"] = a.Name
+	a.fieldMap["backup_type"] = a.BackupType
+	a.fieldMap["storage_type"] = a.StorageType
+	a.fieldMap["backup_path"] = a.BackupPath
+	a.fieldMap["storage_path"] = a.StoragePath
+	a.fieldMap["cron_expression"] = a.CronExpression
+	a.fieldMap["enabled"] = a.Enabled
+	a.fieldMap["last_backup_time"] = a.LastBackupTime
+	a.fieldMap["last_backup_status"] = a.LastBackupStatus
+	a.fieldMap["last_backup_error"] = a.LastBackupError
+	a.fieldMap["s3_endpoint"] = a.S3Endpoint
+	a.fieldMap["s3_access_key_id"] = a.S3AccessKeyID
+	a.fieldMap["s3_secret_access_key"] = a.S3SecretAccessKey
+	a.fieldMap["s3_bucket"] = a.S3Bucket
+	a.fieldMap["s3_region"] = a.S3Region
+}
+
+func (a autoBackup) clone(db *gorm.DB) autoBackup {
+	a.autoBackupDo.ReplaceConnPool(db.Statement.ConnPool)
+	return a
+}
+
+func (a autoBackup) replaceDB(db *gorm.DB) autoBackup {
+	a.autoBackupDo.ReplaceDB(db)
+	return a
+}
+
+type autoBackupDo struct{ gen.DO }
+
+// FirstByID Where("id=@id")
+func (a autoBackupDo) FirstByID(id uint64) (result *model.AutoBackup, err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = a.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+// DeleteByID update @@table set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=@id
+func (a autoBackupDo) DeleteByID(id uint64) (err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("update auto_backups set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = a.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+func (a autoBackupDo) Debug() *autoBackupDo {
+	return a.withDO(a.DO.Debug())
+}
+
+func (a autoBackupDo) WithContext(ctx context.Context) *autoBackupDo {
+	return a.withDO(a.DO.WithContext(ctx))
+}
+
+func (a autoBackupDo) ReadDB() *autoBackupDo {
+	return a.Clauses(dbresolver.Read)
+}
+
+func (a autoBackupDo) WriteDB() *autoBackupDo {
+	return a.Clauses(dbresolver.Write)
+}
+
+func (a autoBackupDo) Session(config *gorm.Session) *autoBackupDo {
+	return a.withDO(a.DO.Session(config))
+}
+
+func (a autoBackupDo) Clauses(conds ...clause.Expression) *autoBackupDo {
+	return a.withDO(a.DO.Clauses(conds...))
+}
+
+func (a autoBackupDo) Returning(value interface{}, columns ...string) *autoBackupDo {
+	return a.withDO(a.DO.Returning(value, columns...))
+}
+
+func (a autoBackupDo) Not(conds ...gen.Condition) *autoBackupDo {
+	return a.withDO(a.DO.Not(conds...))
+}
+
+func (a autoBackupDo) Or(conds ...gen.Condition) *autoBackupDo {
+	return a.withDO(a.DO.Or(conds...))
+}
+
+func (a autoBackupDo) Select(conds ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.Select(conds...))
+}
+
+func (a autoBackupDo) Where(conds ...gen.Condition) *autoBackupDo {
+	return a.withDO(a.DO.Where(conds...))
+}
+
+func (a autoBackupDo) Order(conds ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.Order(conds...))
+}
+
+func (a autoBackupDo) Distinct(cols ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.Distinct(cols...))
+}
+
+func (a autoBackupDo) Omit(cols ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.Omit(cols...))
+}
+
+func (a autoBackupDo) Join(table schema.Tabler, on ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.Join(table, on...))
+}
+
+func (a autoBackupDo) LeftJoin(table schema.Tabler, on ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.LeftJoin(table, on...))
+}
+
+func (a autoBackupDo) RightJoin(table schema.Tabler, on ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.RightJoin(table, on...))
+}
+
+func (a autoBackupDo) Group(cols ...field.Expr) *autoBackupDo {
+	return a.withDO(a.DO.Group(cols...))
+}
+
+func (a autoBackupDo) Having(conds ...gen.Condition) *autoBackupDo {
+	return a.withDO(a.DO.Having(conds...))
+}
+
+func (a autoBackupDo) Limit(limit int) *autoBackupDo {
+	return a.withDO(a.DO.Limit(limit))
+}
+
+func (a autoBackupDo) Offset(offset int) *autoBackupDo {
+	return a.withDO(a.DO.Offset(offset))
+}
+
+func (a autoBackupDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *autoBackupDo {
+	return a.withDO(a.DO.Scopes(funcs...))
+}
+
+func (a autoBackupDo) Unscoped() *autoBackupDo {
+	return a.withDO(a.DO.Unscoped())
+}
+
+func (a autoBackupDo) Create(values ...*model.AutoBackup) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return a.DO.Create(values)
+}
+
+func (a autoBackupDo) CreateInBatches(values []*model.AutoBackup, batchSize int) error {
+	return a.DO.CreateInBatches(values, batchSize)
+}
+
+// Save : !!! underlying implementation is different with GORM
+// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
+func (a autoBackupDo) Save(values ...*model.AutoBackup) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return a.DO.Save(values)
+}
+
+func (a autoBackupDo) First() (*model.AutoBackup, error) {
+	if result, err := a.DO.First(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.AutoBackup), nil
+	}
+}
+
+func (a autoBackupDo) Take() (*model.AutoBackup, error) {
+	if result, err := a.DO.Take(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.AutoBackup), nil
+	}
+}
+
+func (a autoBackupDo) Last() (*model.AutoBackup, error) {
+	if result, err := a.DO.Last(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.AutoBackup), nil
+	}
+}
+
+func (a autoBackupDo) Find() ([]*model.AutoBackup, error) {
+	result, err := a.DO.Find()
+	return result.([]*model.AutoBackup), err
+}
+
+func (a autoBackupDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AutoBackup, err error) {
+	buf := make([]*model.AutoBackup, 0, batchSize)
+	err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
+		defer func() { results = append(results, buf...) }()
+		return fc(tx, batch)
+	})
+	return results, err
+}
+
+func (a autoBackupDo) FindInBatches(result *[]*model.AutoBackup, batchSize int, fc func(tx gen.Dao, batch int) error) error {
+	return a.DO.FindInBatches(result, batchSize, fc)
+}
+
+func (a autoBackupDo) Attrs(attrs ...field.AssignExpr) *autoBackupDo {
+	return a.withDO(a.DO.Attrs(attrs...))
+}
+
+func (a autoBackupDo) Assign(attrs ...field.AssignExpr) *autoBackupDo {
+	return a.withDO(a.DO.Assign(attrs...))
+}
+
+func (a autoBackupDo) Joins(fields ...field.RelationField) *autoBackupDo {
+	for _, _f := range fields {
+		a = *a.withDO(a.DO.Joins(_f))
+	}
+	return &a
+}
+
+func (a autoBackupDo) Preload(fields ...field.RelationField) *autoBackupDo {
+	for _, _f := range fields {
+		a = *a.withDO(a.DO.Preload(_f))
+	}
+	return &a
+}
+
+func (a autoBackupDo) FirstOrInit() (*model.AutoBackup, error) {
+	if result, err := a.DO.FirstOrInit(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.AutoBackup), nil
+	}
+}
+
+func (a autoBackupDo) FirstOrCreate() (*model.AutoBackup, error) {
+	if result, err := a.DO.FirstOrCreate(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.AutoBackup), nil
+	}
+}
+
+func (a autoBackupDo) FindByPage(offset int, limit int) (result []*model.AutoBackup, count int64, err error) {
+	result, err = a.Offset(offset).Limit(limit).Find()
+	if err != nil {
+		return
+	}
+
+	if size := len(result); 0 < limit && 0 < size && size < limit {
+		count = int64(size + offset)
+		return
+	}
+
+	count, err = a.Offset(-1).Limit(-1).Count()
+	return
+}
+
+func (a autoBackupDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
+	count, err = a.Count()
+	if err != nil {
+		return
+	}
+
+	err = a.Offset(offset).Limit(limit).Scan(result)
+	return
+}
+
+func (a autoBackupDo) Scan(result interface{}) (err error) {
+	return a.DO.Scan(result)
+}
+
+func (a autoBackupDo) Delete(models ...*model.AutoBackup) (result gen.ResultInfo, err error) {
+	return a.DO.Delete(models)
+}
+
+func (a *autoBackupDo) withDO(do gen.Dao) *autoBackupDo {
+	a.DO = *do.(*gen.DO)
+	return a
+}

+ 8 - 0
query/gen.go

@@ -19,6 +19,7 @@ var (
 	Q              = new(Query)
 	AcmeUser       *acmeUser
 	AuthToken      *authToken
+	AutoBackup     *autoBackup
 	BanIP          *banIP
 	Cert           *cert
 	ChatGPTLog     *chatGPTLog
@@ -39,6 +40,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 	*Q = *Use(db, opts...)
 	AcmeUser = &Q.AcmeUser
 	AuthToken = &Q.AuthToken
+	AutoBackup = &Q.AutoBackup
 	BanIP = &Q.BanIP
 	Cert = &Q.Cert
 	ChatGPTLog = &Q.ChatGPTLog
@@ -60,6 +62,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 		db:             db,
 		AcmeUser:       newAcmeUser(db, opts...),
 		AuthToken:      newAuthToken(db, opts...),
+		AutoBackup:     newAutoBackup(db, opts...),
 		BanIP:          newBanIP(db, opts...),
 		Cert:           newCert(db, opts...),
 		ChatGPTLog:     newChatGPTLog(db, opts...),
@@ -82,6 +85,7 @@ type Query struct {
 
 	AcmeUser       acmeUser
 	AuthToken      authToken
+	AutoBackup     autoBackup
 	BanIP          banIP
 	Cert           cert
 	ChatGPTLog     chatGPTLog
@@ -105,6 +109,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
 		db:             db,
 		AcmeUser:       q.AcmeUser.clone(db),
 		AuthToken:      q.AuthToken.clone(db),
+		AutoBackup:     q.AutoBackup.clone(db),
 		BanIP:          q.BanIP.clone(db),
 		Cert:           q.Cert.clone(db),
 		ChatGPTLog:     q.ChatGPTLog.clone(db),
@@ -135,6 +140,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 		db:             db,
 		AcmeUser:       q.AcmeUser.replaceDB(db),
 		AuthToken:      q.AuthToken.replaceDB(db),
+		AutoBackup:     q.AutoBackup.replaceDB(db),
 		BanIP:          q.BanIP.replaceDB(db),
 		Cert:           q.Cert.replaceDB(db),
 		ChatGPTLog:     q.ChatGPTLog.replaceDB(db),
@@ -155,6 +161,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 type queryCtx struct {
 	AcmeUser       *acmeUserDo
 	AuthToken      *authTokenDo
+	AutoBackup     *autoBackupDo
 	BanIP          *banIPDo
 	Cert           *certDo
 	ChatGPTLog     *chatGPTLogDo
@@ -175,6 +182,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
 	return &queryCtx{
 		AcmeUser:       q.AcmeUser.WithContext(ctx),
 		AuthToken:      q.AuthToken.WithContext(ctx),
+		AutoBackup:     q.AutoBackup.WithContext(ctx),
 		BanIP:          q.BanIP.WithContext(ctx),
 		Cert:           q.Cert.WithContext(ctx),
 		ChatGPTLog:     q.ChatGPTLog.WithContext(ctx),

+ 4 - 1
router/routers.go

@@ -6,6 +6,7 @@ import (
 	"github.com/gin-contrib/pprof"
 
 	"github.com/0xJacky/Nginx-UI/api/analytic"
+	"github.com/0xJacky/Nginx-UI/api/backup"
 	"github.com/0xJacky/Nginx-UI/api/certificate"
 	"github.com/0xJacky/Nginx-UI/api/cluster"
 	"github.com/0xJacky/Nginx-UI/api/config"
@@ -52,10 +53,11 @@ func InitRouter() {
 		public.InitRouter(root)
 		crypto.InitPublicRouter(root)
 		user.InitAuthRouter(root)
-		
+
 		system.InitPublicRouter(root)
 		system.InitBackupRestoreRouter(root)
 		system.InitSelfCheckRouter(root)
+		backup.InitRouter(root)
 
 		// Authorization required and not websocket request
 		g := root.Group("/", middleware.AuthRequired(), middleware.Proxy())
@@ -80,6 +82,7 @@ func InitRouter() {
 			cluster.InitRouter(g)
 			notification.InitRouter(g)
 			external_notify.InitRouter(g)
+			backup.InitAutoBackupRouter(g)
 		}
 
 		// Authorization required and websocket request

+ 32 - 0
settings/backup.go

@@ -0,0 +1,32 @@
+package settings
+
+// Backup contains configuration settings for backup operations.
+// This structure defines security constraints and access permissions for backup functionality.
+type Backup struct {
+	// GrantedAccessPath defines the list of directory paths that are allowed for backup operations.
+	// All backup source paths and storage destination paths must be within one of these directories.
+	// This security measure prevents unauthorized access to sensitive system directories.
+	//
+	// Examples:
+	//   - "/tmp" - Allow backups in temporary directory
+	//   - "/var/backups" - Allow backups in system backup directory
+	//   - "/home/user/backups" - Allow backups in user's backup directory
+	//
+	// Note: Paths are checked using prefix matching, so "/tmp" allows "/tmp/backup" but not "/tmpfoo"
+	GrantedAccessPath []string `json:"granted_access_path" ini:",,allowshadow"`
+}
+
+// BackupSettings is the global configuration instance for backup operations.
+// This variable holds the current backup security settings and access permissions.
+//
+// Default configuration:
+//   - GrantedAccessPath: Empty list (no paths allowed by default for security)
+//
+// To enable backup functionality, administrators must explicitly configure allowed paths
+// through the settings interface or configuration file.
+var BackupSettings = &Backup{
+	GrantedAccessPath: []string{
+		// Default paths can be added here, but empty for security by default
+		// Example: "/tmp", "/var/backups"
+	},
+}

+ 2 - 0
settings/settings.go

@@ -38,6 +38,7 @@ var envPrefixMap = map[string]interface{}{
 	"OPENAI":    OpenAISettings,
 	"TERMINAL":  TerminalSettings,
 	"WEBAUTHN":  WebAuthnSettings,
+	"BACKUP":    BackupSettings,
 }
 
 func init() {
@@ -46,6 +47,7 @@ func init() {
 
 	sections.Set("database", DatabaseSettings)
 	sections.Set("auth", AuthSettings)
+	sections.Set("backup", BackupSettings)
 	sections.Set("casdoor", CasdoorSettings)
 	sections.Set("cert", CertSettings)
 	sections.Set("cluster", ClusterSettings)