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": {
     "i18n-gettext.translatorConfig": {
     "onlyTranslateUntranslatedAndFuzzy": true,
     "onlyTranslateUntranslatedAndFuzzy": true,
     "batch": {
     "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 (
 import (
 	"bytes"
 	"bytes"

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

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

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

@@ -1,4 +1,4 @@
-package system
+package backup
 
 
 import (
 import (
 	"encoding/base64"
 	"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/release", GetRelease)
 	r.GET("upgrade/current", GetCurrentVersion)
 	r.GET("upgrade/current", GetCurrentVersion)
 
 
-	r.GET("system/backup", CreateBackup)
 	r.GET("system/processing", GetProcessingStatus)
 	r.GET("system/processing", GetProcessingStatus)
 }
 }
 
 
@@ -36,11 +35,7 @@ func InitSelfCheckRouter(r *gin.RouterGroup) {
 }
 }
 
 
 func InitBackupRestoreRouter(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) {
 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']
     ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
     AComment: typeof import('ant-design-vue/es')['Comment']
     AComment: typeof import('ant-design-vue/es')['Comment']
     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
     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']
     ADivider: typeof import('ant-design-vue/es')['Divider']
     ADrawer: typeof import('ant-design-vue/es')['Drawer']
     ADrawer: typeof import('ant-design-vue/es')['Drawer']
     ADropdown: typeof import('ant-design-vue/es')['Dropdown']
     ADropdown: typeof import('ant-design-vue/es')['Dropdown']
@@ -46,16 +44,12 @@ declare module 'vue' {
     AMenu: typeof import('ant-design-vue/es')['Menu']
     AMenu: typeof import('ant-design-vue/es')['Menu']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
     AModal: typeof import('ant-design-vue/es')['Modal']
     AModal: typeof import('ant-design-vue/es')['Modal']
-    APagination: typeof import('ant-design-vue/es')['Pagination']
     APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
     APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
     APopover: typeof import('ant-design-vue/es')['Popover']
     APopover: typeof import('ant-design-vue/es')['Popover']
     AProgress: typeof import('ant-design-vue/es')['Progress']
     AProgress: typeof import('ant-design-vue/es')['Progress']
     AQrcode: typeof import('ant-design-vue/es')['QRCode']
     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']
     AResult: typeof import('ant-design-vue/es')['Result']
     ARow: typeof import('ant-design-vue/es')['Row']
     ARow: typeof import('ant-design-vue/es')['Row']
-    ASegmented: typeof import('ant-design-vue/es')['Segmented']
     ASelect: typeof import('ant-design-vue/es')['Select']
     ASelect: typeof import('ant-design-vue/es')['Select']
     ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
     ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
     ASpace: typeof import('ant-design-vue/es')['Space']
     ASpace: typeof import('ant-design-vue/es')['Space']
@@ -111,18 +105,6 @@ declare module 'vue' {
     SelfCheckSelfCheckHeaderBanner: typeof import('./src/components/SelfCheck/SelfCheckHeaderBanner.vue')['default']
     SelfCheckSelfCheckHeaderBanner: typeof import('./src/components/SelfCheck/SelfCheckHeaderBanner.vue')['default']
     SensitiveStringSensitiveString: typeof import('./src/components/SensitiveString/SensitiveString.vue')['default']
     SensitiveStringSensitiveString: typeof import('./src/components/SensitiveString/SensitiveString.vue')['default']
     SetLanguageSetLanguage: typeof import('./src/components/SetLanguage/SetLanguage.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']
     SwitchAppearanceIconsVPIconMoon: typeof import('./src/components/SwitchAppearance/icons/VPIconMoon.vue')['default']
     SwitchAppearanceIconsVPIconSun: typeof import('./src/components/SwitchAppearance/icons/VPIconSun.vue')['default']
     SwitchAppearanceIconsVPIconSun: typeof import('./src/components/SwitchAppearance/icons/VPIconSun.vue')['default']
     SwitchAppearanceSwitchAppearance: typeof import('./src/components/SwitchAppearance/SwitchAppearance.vue')['default']
     SwitchAppearanceSwitchAppearance: typeof import('./src/components/SwitchAppearance/SwitchAppearance.vue')['default']

+ 121 - 115
app/pnpm-lock.yaml

@@ -847,16 +847,16 @@ packages:
     peerDependencies:
     peerDependencies:
       vue: '>=3'
       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'}
     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'}
     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'}
     engines: {node: '>= 16'}
 
 
   '@isaacs/cliui@8.0.2':
   '@isaacs/cliui@8.0.2':
@@ -918,103 +918,103 @@ packages:
   '@rolldown/pluginutils@1.0.0-beta.9':
   '@rolldown/pluginutils@1.0.0-beta.9':
     resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==}
     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]
     cpu: [arm]
     os: [android]
     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]
     cpu: [arm64]
     os: [android]
     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]
     cpu: [arm64]
     os: [darwin]
     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]
     cpu: [x64]
     os: [darwin]
     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]
     cpu: [arm64]
     os: [freebsd]
     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]
     cpu: [x64]
     os: [freebsd]
     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]
     cpu: [arm]
     os: [linux]
     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]
     cpu: [arm]
     os: [linux]
     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]
     cpu: [arm64]
     os: [linux]
     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]
     cpu: [arm64]
     os: [linux]
     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]
     cpu: [loong64]
     os: [linux]
     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]
     cpu: [ppc64]
     os: [linux]
     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]
     cpu: [riscv64]
     os: [linux]
     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]
     cpu: [riscv64]
     os: [linux]
     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]
     cpu: [s390x]
     os: [linux]
     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]
     cpu: [x64]
     os: [linux]
     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]
     cpu: [x64]
     os: [linux]
     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]
     cpu: [arm64]
     os: [win32]
     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]
     cpu: [ia32]
     os: [win32]
     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]
     cpu: [x64]
     os: [win32]
     os: [win32]
 
 
@@ -1024,8 +1024,8 @@ packages:
   '@simplewebauthn/browser@13.1.0':
   '@simplewebauthn/browser@13.1.0':
     resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==}
     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}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
     peerDependencies:
       eslint: '>=9.0.0'
       eslint: '>=9.0.0'
@@ -2147,6 +2147,10 @@ packages:
   eslint-flat-config-utils@2.1.0:
   eslint-flat-config-utils@2.1.0:
     resolution: {integrity: sha512-6fjOJ9tS0k28ketkUcQ+kKptB4dBZY2VijMZ9rGn8Cwnn1SH0cZBoPXT8AHBFHxmHcLFQK9zbELDinZ2Mr1rng==}
     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:
   eslint-import-resolver-node@0.3.9:
     resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
     resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
 
 
@@ -2171,8 +2175,8 @@ packages:
     peerDependencies:
     peerDependencies:
       eslint: '*'
       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:
     peerDependencies:
       eslint: '*'
       eslint: '*'
 
 
@@ -2182,8 +2186,8 @@ packages:
     peerDependencies:
     peerDependencies:
       eslint: '>=8'
       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}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
     peerDependencies:
     peerDependencies:
       eslint: ^8.57.0 || ^9.0.0
       eslint: ^8.57.0 || ^9.0.0
@@ -3457,8 +3461,8 @@ packages:
   rfdc@1.4.1:
   rfdc@1.4.1:
     resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
     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'}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     hasBin: true
 
 
@@ -3971,8 +3975,8 @@ packages:
     peerDependencies:
     peerDependencies:
       vue: ^3.4.37
       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'}
     engines: {node: '>= 16'}
     peerDependencies:
     peerDependencies:
       vue: ^3.0.0
       vue: ^3.0.0
@@ -4155,7 +4159,7 @@ snapshots:
       '@clack/prompts': 0.10.1
       '@clack/prompts': 0.10.1
       '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.27.0(jiti@2.4.2))
       '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.27.0(jiti@2.4.2))
       '@eslint/markdown': 6.4.0
       '@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/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)
       '@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)
       '@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-flat-config-utils: 2.1.0
       eslint-merge-processors: 2.0.0(eslint@9.27.0(jiti@2.4.2))
       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-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-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-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))
       eslint-plugin-n: 17.18.0(eslint@9.27.0(jiti@2.4.2))
@@ -4696,17 +4700,17 @@ snapshots:
       '@iconify/types': 2.0.0
       '@iconify/types': 2.0.0
       vue: 3.5.14(typescript@5.8.3)
       vue: 3.5.14(typescript@5.8.3)
 
 
-  '@intlify/core-base@11.1.3':
+  '@intlify/core-base@11.1.4':
     dependencies:
     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:
     dependencies:
-      '@intlify/shared': 11.1.3
+      '@intlify/shared': 11.1.4
       source-map-js: 1.2.1
       source-map-js: 1.2.1
 
 
-  '@intlify/shared@11.1.3': {}
+  '@intlify/shared@11.1.4': {}
 
 
   '@isaacs/cliui@8.0.2':
   '@isaacs/cliui@8.0.2':
     dependencies:
     dependencies:
@@ -4793,64 +4797,64 @@ snapshots:
 
 
   '@rolldown/pluginutils@1.0.0-beta.9': {}
   '@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
     optional: true
 
 
-  '@rollup/rollup-android-arm64@4.41.0':
+  '@rollup/rollup-android-arm64@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-darwin-arm64@4.41.0':
+  '@rollup/rollup-darwin-arm64@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-darwin-x64@4.41.0':
+  '@rollup/rollup-darwin-x64@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-freebsd-arm64@4.41.0':
+  '@rollup/rollup-freebsd-arm64@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-freebsd-x64@4.41.0':
+  '@rollup/rollup-freebsd-x64@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-arm-gnueabihf@4.41.0':
+  '@rollup/rollup-linux-arm-gnueabihf@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-arm-musleabihf@4.41.0':
+  '@rollup/rollup-linux-arm-musleabihf@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-arm64-gnu@4.41.0':
+  '@rollup/rollup-linux-arm64-gnu@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-arm64-musl@4.41.0':
+  '@rollup/rollup-linux-arm64-musl@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-loongarch64-gnu@4.41.0':
+  '@rollup/rollup-linux-loongarch64-gnu@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-powerpc64le-gnu@4.41.0':
+  '@rollup/rollup-linux-powerpc64le-gnu@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-riscv64-gnu@4.41.0':
+  '@rollup/rollup-linux-riscv64-gnu@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-riscv64-musl@4.41.0':
+  '@rollup/rollup-linux-riscv64-musl@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-s390x-gnu@4.41.0':
+  '@rollup/rollup-linux-s390x-gnu@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-x64-gnu@4.41.0':
+  '@rollup/rollup-linux-x64-gnu@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-linux-x64-musl@4.41.0':
+  '@rollup/rollup-linux-x64-musl@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-win32-arm64-msvc@4.41.0':
+  '@rollup/rollup-win32-arm64-msvc@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-win32-ia32-msvc@4.41.0':
+  '@rollup/rollup-win32-ia32-msvc@4.41.1':
     optional: true
     optional: true
 
 
-  '@rollup/rollup-win32-x64-msvc@4.41.0':
+  '@rollup/rollup-win32-x64-msvc@4.41.1':
     optional: true
     optional: true
 
 
   '@simonwep/pickr@1.8.2':
   '@simonwep/pickr@1.8.2':
@@ -4860,7 +4864,7 @@ snapshots:
 
 
   '@simplewebauthn/browser@13.1.0': {}
   '@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:
     dependencies:
       '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       '@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)
       eslint: 9.27.0(jiti@2.4.2)
@@ -5240,7 +5244,7 @@ snapshots:
       scroll-into-view-if-needed: 3.1.0
       scroll-into-view-if-needed: 3.1.0
       sortablejs: 1.15.6
       sortablejs: 1.15.6
       vue: 3.5.14(typescript@5.8.3)
       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-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))
       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
       xlsx: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz
@@ -6180,6 +6184,8 @@ snapshots:
     dependencies:
     dependencies:
       pathe: 2.0.3
       pathe: 2.0.3
 
 
+  eslint-import-context@0.1.3: {}
+
   eslint-import-resolver-node@0.3.9:
   eslint-import-resolver-node@0.3.9:
     dependencies:
     dependencies:
       debug: 3.2.7
       debug: 3.2.7
@@ -6202,7 +6208,7 @@ snapshots:
     dependencies:
     dependencies:
       eslint: 9.27.0(jiti@2.4.2)
       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:
     dependencies:
       '@es-joy/jsdoccomment': 0.50.2
       '@es-joy/jsdoccomment': 0.50.2
       eslint: 9.27.0(jiti@2.4.2)
       eslint: 9.27.0(jiti@2.4.2)
@@ -6214,14 +6220,14 @@ snapshots:
       eslint: 9.27.0(jiti@2.4.2)
       eslint: 9.27.0(jiti@2.4.2)
       eslint-compat-utils: 0.5.1(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:
     dependencies:
       '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       '@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)
       comment-parser: 1.4.1
       comment-parser: 1.4.1
       debug: 4.4.1
       debug: 4.4.1
       eslint: 9.27.0(jiti@2.4.2)
       eslint: 9.27.0(jiti@2.4.2)
+      eslint-import-context: 0.1.3
       eslint-import-resolver-node: 0.3.9
       eslint-import-resolver-node: 0.3.9
-      get-tsconfig: 4.10.1
       is-glob: 4.0.3
       is-glob: 4.0.3
       minimatch: 10.0.1
       minimatch: 10.0.1
       semver: 7.7.2
       semver: 7.7.2
@@ -7750,30 +7756,30 @@ snapshots:
 
 
   rfdc@1.4.1: {}
   rfdc@1.4.1: {}
 
 
-  rollup@4.41.0:
+  rollup@4.41.1:
     dependencies:
     dependencies:
       '@types/estree': 1.0.7
       '@types/estree': 1.0.7
     optionalDependencies:
     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
       fsevents: 2.3.3
 
 
   run-applescript@7.0.0: {}
   run-applescript@7.0.0: {}
@@ -8368,7 +8374,7 @@ snapshots:
       fdir: 6.4.4(picomatch@4.0.2)
       fdir: 6.4.4(picomatch@4.0.2)
       picomatch: 4.0.2
       picomatch: 4.0.2
       postcss: 8.5.3
       postcss: 8.5.3
-      rollup: 4.41.0
+      rollup: 4.41.1
       tinyglobby: 0.2.13
       tinyglobby: 0.2.13
     optionalDependencies:
     optionalDependencies:
       '@types/node': 22.15.21
       '@types/node': 22.15.21
@@ -8402,10 +8408,10 @@ snapshots:
     dependencies:
     dependencies:
       vue: 3.5.14(typescript@5.8.3)
       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:
     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/devtools-api': 6.6.4
       vue: 3.5.14(typescript@5.8.3)
       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
  * Interface for restore backup response
@@ -21,13 +22,34 @@ export interface RestoreOptions {
   verify_hash: boolean
   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 = {
 const backup = {
   /**
   /**
    * Create and download a backup of nginx-ui and nginx configurations
    * Create and download a backup of nginx-ui and nginx configurations
    * Use http module with returnFullResponse option to access headers
    * Use http module with returnFullResponse option to access headers
    */
    */
   createBackup() {
   createBackup() {
-    return http.get('/system/backup', {
+    return http.get('/backup', {
       responseType: 'blob',
       responseType: 'blob',
       returnFullResponse: true,
       returnFullResponse: true,
     })
     })
@@ -45,7 +67,7 @@ const backup = {
     formData.append('restore_nginx_ui', options.restore_nginx_ui.toString())
     formData.append('restore_nginx_ui', options.restore_nginx_ui.toString())
     formData.append('verify_hash', options.verify_hash.toString())
     formData.append('verify_hash', options.verify_hash.toString())
 
 
-    return http.post('/system/backup/restore', formData, {
+    return http.post('/restore', formData, {
       headers: {
       headers: {
         'Content-Type': 'multipart/form-data;charset=UTF-8',
         '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
 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}'),
   4802: () => $gettext('Failed to calculate Nginx UI hash: {0}'),
   4803: () => $gettext('Failed to calculate Nginx hash: {0}'),
   4803: () => $gettext('Failed to calculate Nginx hash: {0}'),
   4804: () => $gettext('Hash verification failed: file integrity compromised'),
   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"
 msgid "2FA Settings"
 msgstr "إعدادات المصادقة الثنائية"
 msgstr "إعدادات المصادقة الثنائية"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "عن"
 msgstr "عن"
 
 
@@ -133,6 +133,7 @@ msgstr "مستخدم ACME"
 msgid "Action"
 msgid "Action"
 msgstr "إجراء"
 msgstr "إجراء"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -345,6 +346,11 @@ msgstr "آلي"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "التحديث التلقائي"
 msgstr "التحديث التلقائي"
@@ -386,7 +392,7 @@ msgstr "العودة إلى الصفحة الرئيسية"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "العودة إلى القائمة"
 msgstr "العودة إلى القائمة"
 
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgid "Backup"
 msgstr "النسخ الاحتياطي"
 msgstr "النسخ الاحتياطي"
 
 
@@ -398,10 +404,38 @@ msgstr "فشل التحقق من سلامة ملف النسخ الاحتياطي
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr "لم يتم العثور على ملف النسخ الاحتياطي: {0}"
 msgstr "لم يتم العثور على ملف النسخ الاحتياطي: {0}"
 
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgid "Backup has been downloaded successfully"
 msgstr "تم تنزيل النسخة الاحتياطية بنجاح"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "دقائق حد الحظر"
 msgstr "دقائق حد الحظر"
@@ -457,6 +491,14 @@ msgstr "فيما يلي العناصر المحددة التي تريد تعدي
 msgid "Block is nil"
 msgid "Block is nil"
 msgstr "الكتلة فارغة"
 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "بناء مع"
 msgstr "بناء مع"
@@ -528,6 +570,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "إلغاء"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgid "Cannot change initial user password in demo mode"
 msgstr "لا يمكن تغيير كلمة مرور المستخدم الأولي في وضع التجربة"
 msgstr "لا يمكن تغيير كلمة مرور المستخدم الأولي في وضع التجربة"
@@ -974,12 +1024,12 @@ msgstr "محتوى"
 msgid "Copied"
 msgid "Copied"
 msgstr "تم النسخ"
 msgstr "تم النسخ"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "تم النسخ!"
 msgstr "تم النسخ!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "نسخ"
 msgstr "نسخ"
 
 
@@ -1015,7 +1065,7 @@ msgstr "إنشاء"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "إنشاء آخر"
 msgstr "إنشاء آخر"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "إنشاء نسخة احتياطية"
 msgstr "إنشاء نسخة احتياطية"
 
 
@@ -1027,7 +1077,7 @@ msgstr "إنشاء ملف"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "إنشاء مجلد"
 msgstr "إنشاء مجلد"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "Backup files will be automatically downloaded to your computer."
@@ -1035,6 +1085,7 @@ msgstr ""
 "إنشاء نسخ احتياطية للنظام تتضمن تكوين Nginx وإعدادات واجهة مستخدم Nginx. "
 "إنشاء نسخ احتياطية للنظام تتضمن تكوين Nginx وإعدادات واجهة مستخدم Nginx. "
 "سيتم تنزيل ملفات النسخ الاحتياطي تلقائيًا إلى جهاز الكمبيوتر الخاص بك."
 "سيتم تنزيل ملفات النسخ الاحتياطي تلقائيًا إلى جهاز الكمبيوتر الخاص بك."
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1059,6 +1110,10 @@ msgstr "بيان الاعتماد"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "بيانات الاعتماد"
 msgstr "بيانات الاعتماد"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "تعبير كرون"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "TOTP مفعل للحساب الحالي."
 msgstr "TOTP مفعل للحساب الحالي."
@@ -1092,17 +1147,42 @@ msgstr "الإصدار الحالي"
 msgid "Custom"
 msgid "Custom"
 msgstr "مخصص"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 "indicator."
 msgstr "قم بتخصيص اسم العقدة المحلية ليتم عرضها في مؤشر البيئة."
 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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "لوحة المعلومات"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "أيام"
 msgstr "أيام"
@@ -1300,6 +1380,7 @@ msgstr "فشل تعطيل الدفق %{name} من %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "تم تعطيل الدفق %{name} من %{node} بنجاح"
 msgstr "تم تعطيل الدفق %{name} من %{node} بنجاح"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1434,6 +1515,10 @@ msgstr "تم النسخ إلى المحلي بنجاح"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "ديناميكي"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "تعديل"
 msgstr "تعديل"
@@ -1560,6 +1645,8 @@ msgstr "تفعيل TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "تفعيل 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1626,6 +1713,18 @@ msgstr "خطأ في معالجة المحتوى"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "مسار الملف التنفيذي"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1652,6 +1751,11 @@ msgstr "إشعار خارجي"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "فشل في الحصول على الشهادة"
 msgstr "فشل في الحصول على الشهادة"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "فشل"
+
 #: src/constants/errors/docker.ts:4
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "فشل في الاتصال بنموذج التنفيذ: {0}"
 msgstr "فشل في الاتصال بنموذج التنفيذ: {0}"
@@ -1704,6 +1808,10 @@ msgstr "فشل في نسخ دليل تكوين Nginx: {0}"
 msgid "Failed to create backup"
 msgid "Failed to create backup"
 msgstr "فشل إنشاء نسخة احتياطية"
 msgstr "فشل إنشاء نسخة احتياطية"
 
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "فشل في إنشاء دليل النسخ الاحتياطي: {0}"
+
 #: src/constants/errors/backup.ts:12
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "فشل إنشاء ملف النسخ الاحتياطي: {0}"
 msgstr "فشل إنشاء ملف النسخ الاحتياطي: {0}"
@@ -1728,6 +1836,10 @@ msgstr "فشل إنشاء الدليل الرئيسي: {0}"
 msgid "Failed to create restore directory: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "فشل في إنشاء دليل الاستعادة: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "فشل إنشاء رابط رمزي: {0}"
 msgstr "فشل إنشاء رابط رمزي: {0}"
@@ -1940,6 +2052,10 @@ msgstr "فشل بدء الحاوية المؤقتة: {0}"
 msgid "Failed to verify hashes: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "فشل في التحقق من التجزئات: {0}"
 msgstr "فشل في التحقق من التجزئات: {0}"
 
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "فشل في كتابة ملف النسخ الاحتياطي: {0}"
+
 #: src/constants/errors/backup.ts:55
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "فشل في كتابة الملف المفكوك تشفيره: {0}"
 msgstr "فشل في كتابة الملف المفكوك تشفيره: {0}"
@@ -1948,6 +2064,10 @@ msgstr "فشل في كتابة الملف المفكوك تشفيره: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "فشل في كتابة الملف المشفر: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "فشل الكتابة إلى مخزن zip المؤقت: {0}"
 msgstr "فشل الكتابة إلى مخزن zip المؤقت: {0}"
@@ -2003,6 +2123,14 @@ msgstr "تنسيق الكود"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "تم التنسيق بنجاح"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "شهادة عامة"
 msgstr "شهادة عامة"
@@ -2079,7 +2207,7 @@ msgstr "القيمة الأعلى تعني إعادة استخدام أفضل ل
 msgid "History"
 msgid "History"
 msgstr "السجل"
 msgstr "السجل"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "الصفحة الرئيسية"
 msgstr "الصفحة الرئيسية"
 
 
@@ -2087,6 +2215,10 @@ msgstr "الصفحة الرئيسية"
 msgid "Host"
 msgid "Host"
 msgstr "المضيف"
 msgstr "المضيف"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "ساعة"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2266,6 +2398,10 @@ msgstr "حشو غير صالح في البيانات المشفرة"
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "رمز المرور أو رمز الاسترداد غير صالح"
 msgstr "رمز المرور أو رمز الاسترداد غير صالح"
 
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "مسار غير صالح: {0}"
+
 #: src/constants/errors/user.ts:5
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "رمز الاسترداد غير صالح"
 msgstr "رمز الاسترداد غير صالح"
@@ -2335,6 +2471,14 @@ msgstr "لارك"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "لارك المخصص"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "آخر فحص في"
 msgstr "آخر فحص في"
@@ -2428,10 +2572,17 @@ msgstr "جارٍ تحميل البيانات..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "محلي"
 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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "مكان"
 msgstr "مكان"
@@ -2629,6 +2780,10 @@ msgstr "الحد الأدنى للمساحة الحرة"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "المساحة الحرة الدنيا في دليل التخزين المؤقت"
 msgstr "المساحة الحرة الدنيا في دليل التخزين المؤقت"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "دقيقة"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "دقائق"
 msgstr "دقائق"
@@ -2662,11 +2817,24 @@ msgstr "الوحدة"
 msgid "Modules"
 msgid "Modules"
 msgstr "الوحدات"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "توجيه متعدد الأسطر"
 msgstr "توجيه متعدد الأسطر"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2766,6 +2934,11 @@ msgstr "تكوين Nginx لا يتضمن sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "تكوين Nginx لا يتضمن 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "لم يتم تعيين دليل تكوين Nginx"
 msgstr "لم يتم تعيين دليل تكوين Nginx"
@@ -2903,6 +3076,11 @@ msgstr "أقصى أداء نظري لـ Nginx"
 msgid "Nginx UI already installed"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI مثبت بالفعل"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "تمت استعادة تكوين Nginx UI"
 msgstr "تمت استعادة تكوين Nginx UI"
@@ -3078,6 +3256,7 @@ msgstr "غير متصل"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "حسنًا"
 msgstr "حسنًا"
 
 
@@ -3219,6 +3397,10 @@ msgstr "كلمات المرور غير متطابقة"
 msgid "Path"
 msgid "Path"
 msgstr "مسار"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "المسار: {0} ليس ضمن دليل nginx conf: {1}"
 msgstr "المسار: {0} ليس ضمن دليل nginx conf: {1}"
@@ -3227,6 +3409,11 @@ msgstr "المسار: {0} ليس ضمن دليل nginx conf: {1}"
 msgid "Payload resource is nil"
 msgid "Payload resource is nil"
 msgstr "مورد الحمولة فارغ"
 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "نفذ"
 msgstr "نفذ"
@@ -3294,6 +3481,10 @@ msgstr "الرجاء إدخال رمز الأمان الذي تم استلامه
 msgid "Please fill all fields correctly"
 msgid "Please fill all fields correctly"
 msgstr "يرجى ملء جميع الحقول بشكل صحيح"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3348,8 +3539,8 @@ msgstr "يرجى إدخال كلمة المرور الخاصة بك!"
 msgid "Please input your username!"
 msgid "Please input your username!"
 msgstr "يرجى إدخال اسم المستخدم الخاص بك!"
 msgstr "يرجى إدخال اسم المستخدم الخاص بك!"
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "الرجاء تسجيل الدخول."
 msgstr "الرجاء تسجيل الدخول."
 
 
@@ -3361,7 +3552,7 @@ msgstr "يرجى ملاحظة أن تكوين وحدات الوقت أدناه 
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "يرجى حل جميع المشكلات قبل المتابعة مع التثبيت"
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr "يرجى حفظ رمز الأمان هذا، ستحتاج إليه للاستعادة:"
 msgstr "يرجى حفظ رمز الأمان هذا، ستحتاج إليه للاستعادة:"
 
 
@@ -3828,6 +4019,109 @@ msgstr "وضع التشغيل"
 msgid "Running"
 msgid "Running"
 msgstr "يعمل"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "امسح رمز الاستجابة السريعة بهاتفك المحمول لإضافة الحساب إلى التطبيق."
 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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "حزمة تطوير البرمجيات SDK"
 msgstr "حزمة تطوير البرمجيات SDK"
@@ -3930,7 +4232,7 @@ msgstr "إعدادات الأمان"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "رمز الأمان"
 msgstr "رمز الأمان"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "معلومات رمز الأمان"
 msgstr "معلومات رمز الأمان"
 
 
@@ -4187,6 +4489,29 @@ msgstr "متوقف"
 msgid "Storage"
 msgid "Storage"
 msgstr "تخزين"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "تم تمكين البث"
 msgstr "تم تمكين البث"
@@ -4211,10 +4536,17 @@ msgstr "دليل Streams-enabled غير موجود"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "منفذ حالة ستاب"
 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"
 msgid "Success"
 msgstr "نجاح"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4328,7 +4660,7 @@ msgstr "مزامنة"
 msgid "System"
 msgid "System"
 msgstr "نظام"
 msgstr "نظام"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "نسخ احتياطي للنظام"
 msgstr "نسخ احتياطي للنظام"
 
 
@@ -4345,7 +4677,6 @@ msgid "System Restore"
 msgstr "استعادة النظام"
 msgstr "استعادة النظام"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "تم استعادة النظام بنجاح."
 msgstr "تم استعادة النظام بنجاح."
 
 
@@ -4369,6 +4700,10 @@ msgstr "طرفية"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "أمر البدء في المحطة الطرفية"
 msgstr "أمر البدء في المحطة الطرفية"
 
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "اختبار اتصال S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4584,6 +4919,10 @@ msgstr "سيتم ترقية أو إعادة تثبيت Nginx UI على %{nodeNam
 msgid "Throttle"
 msgid "Throttle"
 msgstr "كبح"
 msgstr "كبح"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "الخميس"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4700,6 +5039,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "مهملات"
 msgstr "مهملات"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "الثلاثاء"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "يتطلب المصادقة الثنائية"
 msgstr "يتطلب المصادقة الثنائية"
@@ -4716,6 +5059,10 @@ msgstr "نوع"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "غير معروف"
 msgstr "غير معروف"
 
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "نوع النسخ الاحتياطي غير مدعوم: {0}"
+
 #: src/views/user/UserProfile.vue:218
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "تحديث كلمة المرور"
 msgstr "تحديث كلمة المرور"
@@ -4728,6 +5075,7 @@ msgstr "تحديث الملف الشخصي"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "تم التحديث بنجاح"
 msgstr "تم التحديث بنجاح"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4740,7 +5088,7 @@ msgstr "تم التحديث بنجاح"
 msgid "Updated at"
 msgid "Updated at"
 msgstr "محدث في"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4870,10 +5218,10 @@ msgstr "تمت المشاهدة"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "عمليات الانتظار"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "تحذير"
 msgstr "تحذير"
 
 
@@ -4913,6 +5261,18 @@ msgstr "إعدادات WebAuthn غير مضبوطة"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "خطأ في اتصال WebSocket"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -4957,7 +5317,7 @@ msgstr "عمليات العامل"
 msgid "Workers"
 msgid "Workers"
 msgstr "العمال"
 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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "مساحة العمل"
 msgstr "مساحة العمل"
@@ -5041,6 +5401,9 @@ msgstr "رموزك القديمة لن تعمل بعد الآن."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "مفاتيح المرور الخاصة بك"
 msgstr "مفاتيح المرور الخاصة بك"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "خطأ آخر نسخة احتياطية"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "تطبيق"
 #~ msgstr "تطبيق"
 
 
@@ -5074,9 +5437,6 @@ msgstr "مفاتيح المرور الخاصة بك"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "حسنًا"
 #~ msgstr "حسنًا"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "يرجى ملء الحقول المطلوبة"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "استعادة"
 #~ msgstr "استعادة"
 
 
@@ -5138,10 +5498,6 @@ msgstr "مفاتيح المرور الخاصة بك"
 #~ msgid "Nginx Conf Include Conf.d"
 #~ msgid "Nginx Conf Include Conf.d"
 #~ msgstr "أمر إعادة تشغيل Nginx"
 #~ msgstr "أمر إعادة تشغيل Nginx"
 
 
-#, fuzzy
-#~ msgid "Sites Directory"
-#~ msgstr "مجلد"
-
 #~ msgid "Format error %{msg}"
 #~ msgid "Format error %{msg}"
 #~ msgstr "خطأ في التنسيق %{msg}"
 #~ msgstr "خطأ في التنسيق %{msg}"
 
 

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

@@ -107,7 +107,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "2FA-Einstellungen"
 msgstr "2FA-Einstellungen"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "Über"
 msgstr "Über"
 
 
@@ -133,6 +133,7 @@ msgstr "ACME-Benutzer"
 msgid "Action"
 msgid "Action"
 msgstr "Aktion"
 msgstr "Aktion"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -351,6 +352,11 @@ msgstr "Auto"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "Auto = CPU -Kerne"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Automatische Aktualisierung"
 msgstr "Automatische Aktualisierung"
@@ -392,7 +398,7 @@ msgstr "Zurück zur Startseite"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Zurück zur Liste"
 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"
 msgid "Backup"
 msgstr "Sicherung"
 msgstr "Sicherung"
 
 
@@ -406,10 +412,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr "Sicherungsdatei nicht gefunden: {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"
 msgid "Backup has been downloaded successfully"
 msgstr "Die Sicherung wurde erfolgreich heruntergeladen"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Schwellenwert für Minuten sperren"
 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"
 msgid "Block is nil"
 msgstr "Block ist 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "Build mit"
 msgstr "Build mit"
@@ -538,6 +582,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Abbrechen"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgid "Cannot change initial user password in demo mode"
 msgstr "Das Passwort des ersten Benutzers kann im Demo-Modus nicht geändert werden"
 msgstr "Das Passwort des ersten Benutzers kann im Demo-Modus nicht geändert werden"
@@ -982,12 +1034,12 @@ msgstr "Inhalt"
 msgid "Copied"
 msgid "Copied"
 msgstr "Kopiert"
 msgstr "Kopiert"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "Kopiert!"
 msgstr "Kopiert!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Kopieren"
 msgstr "Kopieren"
 
 
@@ -1025,7 +1077,7 @@ msgstr "Erstellen"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Weiteres erstellen"
 msgstr "Weiteres erstellen"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Backup erstellen"
 msgstr "Backup erstellen"
 
 
@@ -1037,7 +1089,7 @@ msgstr "Datei erstellen"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Ordner erstellen"
 msgstr "Ordner erstellen"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "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 "
 "der Nginx-UI-Einstellungen. Die Backup-Dateien werden automatisch auf Ihren "
 "Computer heruntergeladen."
 "Computer heruntergeladen."
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1070,6 +1123,10 @@ msgstr "Zugangsdaten"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Zugangsdaten"
 msgstr "Zugangsdaten"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Cron-Ausdruck"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "Aktuelles Konto ist TOTP aktiviert."
 msgstr "Aktuelles Konto ist TOTP aktiviert."
@@ -1103,17 +1160,42 @@ msgstr "Aktuelle Version"
 msgid "Custom"
 msgid "Custom"
 msgstr "Benutzerdefiniert"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 "indicator."
 msgstr "Name des lokalen Knotens anpassen, der im Umgebungsindikator angezeigt wird."
 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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Übersicht"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Tage"
 msgstr "Tage"
@@ -1313,6 +1395,7 @@ msgstr "Deaktivieren des Streams %{name} von %{node} fehlgeschlagen"
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Stream %{name} von %{node} erfolgreich deaktiviert"
 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:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1452,6 +1535,10 @@ msgstr "Erfolgreich lokal dupliziert"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "Dynamisch"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Bearbeiten"
 msgstr "Bearbeiten"
@@ -1578,6 +1665,8 @@ msgstr "Aktiviere TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "TOTP aktivieren"
 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1644,6 +1733,18 @@ msgstr "Fehler bei der Verarbeitung des Inhalts"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Ausführbarer Pfad"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1670,6 +1771,11 @@ msgstr "Externe Benachrichtigung"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Zertifikat konnte nicht abgerufen werden"
 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
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Anfügen an die Ausführungsinstanz fehlgeschlagen: {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"
 msgid "Failed to create backup"
 msgstr "Sicherungskopie konnte nicht erstellt werden"
 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
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Fehler beim Erstellen der Sicherungsdatei: {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}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Fehler beim Erstellen des Wiederherstellungsverzeichnisses: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Symbolische Verknüpfung konnte nicht erstellt werden: {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}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Hash-Überprüfung fehlgeschlagen: {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
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Fehler beim Schreiben der entschlüsselten Datei: {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}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Fehler beim Schreiben der verschlüsselten Datei: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Fehler beim Schreiben in den Zip-Puffer: {0}"
 msgstr "Fehler beim Schreiben in den Zip-Puffer: {0}"
@@ -2023,6 +2145,14 @@ msgstr "Formatcode"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Erfolgreich formatiert"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Allgemeines Zertifikat"
 msgstr "Allgemeines Zertifikat"
@@ -2099,7 +2229,7 @@ msgstr "Ein höherer Wert bedeutet eine bessere Wiederverwendung der Verbindung"
 msgid "History"
 msgid "History"
 msgstr "Verlauf"
 msgstr "Verlauf"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Startseite"
 msgstr "Startseite"
 
 
@@ -2107,6 +2237,10 @@ msgstr "Startseite"
 msgid "Host"
 msgid "Host"
 msgstr "Host"
 msgstr "Host"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Stunde"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2291,6 +2425,10 @@ msgstr "Ungültige Auffüllung in entschlüsselten Daten"
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "Ungültiger Passcode oder Wiederherstellungscode"
 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
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Ungültiger Wiederherstellungscode"
 msgstr "Ungültiger Wiederherstellungscode"
@@ -2360,6 +2498,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Benutzerdefiniert"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "Zuletzt überprüft am"
 msgstr "Zuletzt überprüft am"
@@ -2453,10 +2599,17 @@ msgstr "Daten werden geladen..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "Lokal"
 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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "Ort"
 msgstr "Ort"
@@ -2655,6 +2808,10 @@ msgstr "Mindestfreier Speicherplatz"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "Mindestfreier Speicherplatz im Cache-Verzeichnis"
 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
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Minuten"
 msgstr "Minuten"
@@ -2688,11 +2845,24 @@ msgstr "Modul"
 msgid "Modules"
 msgid "Modules"
 msgstr "Module"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Mehrzeilige Direktive"
 msgstr "Mehrzeilige Direktive"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx-Konfiguration enthält keinen stream-enabled-Ordner"
 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "Das Nginx-Konfigurationsverzeichnis ist nicht festgelegt"
 msgstr "Das Nginx-Konfigurationsverzeichnis ist nicht festgelegt"
@@ -2929,6 +3104,11 @@ msgstr "Theoretische maximale Leistung von Nginx"
 msgid "Nginx UI already installed"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI ist bereits installiert"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "Die Nginx-UI-Konfiguration wurde wiederhergestellt"
 msgstr "Die Nginx-UI-Konfiguration wurde wiederhergestellt"
@@ -3109,6 +3289,7 @@ msgstr "Offline"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -3250,6 +3430,10 @@ msgstr "Passwörter stimmen nicht überein"
 msgid "Path"
 msgid "Path"
 msgstr "Pfad"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Pfad: {0} befindet sich nicht unter dem nginx-Konfigurationsverzeichnis: {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"
 msgid "Payload resource is nil"
 msgstr "Die Nutzlast-Ressource ist null"
 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Ausführen"
 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"
 msgid "Please fill all fields correctly"
 msgstr "Bitte füllen Sie alle Felder korrekt aus"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "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!"
 msgid "Please input your username!"
 msgstr "Bitte gib deinen Benutzernamen ein!"
 msgstr "Bitte gib deinen Benutzernamen ein!"
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Bitte melden Sie sich an."
 msgstr "Bitte melden Sie sich an."
 
 
@@ -3405,7 +3598,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Bitte beheben Sie alle Probleme, bevor Sie mit der Installation fortfahren"
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 "Bitte speichern Sie dieses Sicherheitstoken, Sie benötigen es für die "
 "Bitte speichern Sie dieses Sicherheitstoken, Sie benötigen es für die "
@@ -3882,6 +4075,109 @@ msgstr "Betriebsmodus"
 msgid "Running"
 msgid "Running"
 msgstr "Arbeite"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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."
 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."
 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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3984,7 +4288,7 @@ msgstr "Sicherheitseinstellungen"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Sicherheitstoken"
 msgstr "Sicherheitstoken"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Sicherheitstoken-Informationen"
 msgstr "Sicherheitstoken-Informationen"
 
 
@@ -4247,6 +4551,29 @@ msgstr "Gestoppt"
 msgid "Storage"
 msgid "Storage"
 msgstr "Speicher"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "Stream ist aktiviert"
 msgstr "Stream ist aktiviert"
@@ -4271,10 +4598,17 @@ msgstr "Streams-enabled-Verzeichnis existiert nicht"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "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"
 msgid "Success"
 msgstr "Erfolg"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4393,7 +4727,7 @@ msgstr "Synchronisation"
 msgid "System"
 msgid "System"
 msgstr "System"
 msgstr "System"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "System-Backup"
 msgstr "System-Backup"
 
 
@@ -4410,7 +4744,6 @@ msgid "System Restore"
 msgstr "Systemwiederherstellung"
 msgstr "Systemwiederherstellung"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "System erfolgreich wiederhergestellt."
 msgstr "System erfolgreich wiederhergestellt."
 
 
@@ -4434,6 +4767,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "Terminal-Startbefehl"
 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
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 "
 "Diese Aktion entfernt das Zertifikat nur aus der Datenbank. Die "
 "Zertifikatsdateien im Dateisystem werden nicht gelöscht."
 "Zertifikatsdateien im Dateisystem werden nicht gelöscht."
 
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4654,6 +4991,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Begrenzung"
 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/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4773,6 +5114,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Mülleimer"
 msgstr "Mülleimer"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Dienstag"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Zwei-Faktor-Authentifizierung erforderlich"
 msgstr "Zwei-Faktor-Authentifizierung erforderlich"
@@ -4789,6 +5134,10 @@ msgstr "Typ"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Unbekannt"
 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
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Passwort aktualisieren"
 msgstr "Passwort aktualisieren"
@@ -4801,6 +5150,7 @@ msgstr "Profil aktualisieren"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Erfolgreich aktualisiert"
 msgstr "Erfolgreich aktualisiert"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4813,7 +5163,7 @@ msgstr "Erfolgreich aktualisiert"
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Aktualisiert am"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4943,10 +5293,10 @@ msgstr "Angesehen"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Warteverfahren"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Warnung"
 msgstr "Warnung"
 
 
@@ -4990,6 +5340,18 @@ msgstr "WebAuthn-Einstellungen sind nicht konfiguriert"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "WebSocket-Verbindungsfehler"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5040,7 +5402,7 @@ msgstr "Worker-Prozesse"
 msgid "Workers"
 msgid "Workers"
 msgstr "Worker"
 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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Arbeitsplatz"
 msgstr "Arbeitsplatz"
@@ -5128,6 +5490,9 @@ msgstr "Ihre alten Codes funktionieren nicht mehr."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Deine Passkeys"
 msgstr "Deine Passkeys"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Letzter Sicherungsfehler"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "Anwenden"
 #~ msgstr "Anwenden"
 
 
@@ -5161,9 +5526,6 @@ msgstr "Deine Passkeys"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "OK"
 #~ msgstr "OK"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Bitte fülle die erforderlichen Felder aus"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "Wiederherstellen"
 #~ msgstr "Wiederherstellen"
 
 

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

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

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

@@ -114,7 +114,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Configuración de 2FA"
 msgstr "Configuración de 2FA"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "Acerca de"
 msgstr "Acerca de"
 
 
@@ -140,6 +140,7 @@ msgstr "Usuario ACME"
 msgid "Action"
 msgid "Action"
 msgstr "Acción"
 msgstr "Acción"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -358,6 +359,11 @@ msgstr "Automático"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "auto = núcleos de CPU"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Actualización automática"
 msgstr "Actualización automática"
@@ -399,7 +405,7 @@ msgstr "Volver al Inicio"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Volver a la lista"
 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"
 msgid "Backup"
 msgstr "Copia de seguridad"
 msgstr "Copia de seguridad"
 
 
@@ -413,10 +419,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr "Archivo de respaldo no encontrado: {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"
 msgid "Backup has been downloaded successfully"
 msgstr "La copia de seguridad se ha descargado correctamente"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Umbral de Prohibición en Minutos"
 msgstr "Umbral de Prohibición en Minutos"
@@ -474,6 +510,14 @@ msgstr ""
 msgid "Block is nil"
 msgid "Block is nil"
 msgstr "El bloque es nulo"
 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "Desarrollado con"
 msgstr "Desarrollado con"
@@ -547,6 +591,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Cancelar"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgid "Cannot change initial user password in demo mode"
 msgstr "No se puede cambiar la contraseña del usuario inicial en modo demo"
 msgstr "No se puede cambiar la contraseña del usuario inicial en modo demo"
@@ -991,12 +1043,12 @@ msgstr "Contenido"
 msgid "Copied"
 msgid "Copied"
 msgstr "Copiado"
 msgstr "Copiado"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "¡Copiado!"
 msgstr "¡Copiado!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Copiar"
 msgstr "Copiar"
 
 
@@ -1034,7 +1086,7 @@ msgstr "Crear"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Crear otro"
 msgstr "Crear otro"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Crear copia de seguridad"
 msgstr "Crear copia de seguridad"
 
 
@@ -1046,7 +1098,7 @@ msgstr "Crear Archivo"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Crear carpeta"
 msgstr "Crear carpeta"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "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 "
 "y los ajustes de Nginx UI. Los archivos de respaldo se descargarán "
 "automáticamente en tu computadora."
 "automáticamente en tu computadora."
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1079,6 +1132,10 @@ msgstr "Credencial"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Credenciales"
 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
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "La cuenta actual tiene habilitada TOTP."
 msgstr "La cuenta actual tiene habilitada TOTP."
@@ -1112,6 +1169,15 @@ msgstr "Versión actual"
 msgid "Custom"
 msgid "Custom"
 msgstr "Personalizado"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "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 "
 "Personalice el nombre del servidor local para mostrarlo en el indicador de "
 "entorno."
 "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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Panel"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Días"
 msgstr "Días"
@@ -1324,6 +1406,7 @@ msgstr "Desactivar el flujo %{name} desde %{node} falló"
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Deshabilitar el flujo %{name} desde %{node} con éxito"
 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:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1461,6 +1544,10 @@ msgstr "Duplicado con éxito a local"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "Dinámico"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Editar"
 msgstr "Editar"
@@ -1587,6 +1674,8 @@ msgstr "Habilitar TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "Habilitar 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1653,6 +1742,18 @@ msgstr "Error al procesar el contenido"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Ruta ejecutable"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1679,6 +1780,11 @@ msgstr "Notificación Externa"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Falla al obtener el certificado"
 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
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Error al adjuntar a la instancia de ejecución: {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"
 msgid "Failed to create backup"
 msgstr "No se pudo crear la copia de seguridad"
 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
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Error al crear el archivo de respaldo: {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}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Error al crear el directorio de restauración: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Error al crear el enlace simbólico: {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}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Error al verificar los 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
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Error al escribir el archivo desencriptado: {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}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Error al escribir el archivo cifrado: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Error al escribir en el búfer ZIP: {0}"
 msgstr "Error al escribir en el búfer ZIP: {0}"
@@ -2034,6 +2156,14 @@ msgstr "Código de formato"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Formateado correctamente"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Certificado General"
 msgstr "Certificado General"
@@ -2110,7 +2240,7 @@ msgstr "Un valor más alto significa una mejor reutilización de la conexión"
 msgid "History"
 msgid "History"
 msgstr "Historial"
 msgstr "Historial"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Inicio"
 msgstr "Inicio"
 
 
@@ -2118,6 +2248,10 @@ msgstr "Inicio"
 msgid "Host"
 msgid "Host"
 msgstr "Host"
 msgstr "Host"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Hora"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2298,6 +2432,10 @@ msgstr "Relleno no válido en los datos descifrados"
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "Código de acceso o código de recuperación inválido"
 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
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Código de recuperación no válido"
 msgstr "Código de recuperación no válido"
@@ -2367,6 +2505,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Personalizado"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "Comprobado por última vez el"
 msgstr "Comprobado por última vez el"
@@ -2460,10 +2606,17 @@ msgstr "Cargando datos..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "Ubicación"
 msgstr "Ubicación"
@@ -2663,6 +2816,10 @@ msgstr "Espacio libre mínimo"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "Espacio libre mínimo en el directorio de caché"
 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
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Minutos"
 msgstr "Minutos"
@@ -2696,11 +2853,24 @@ msgstr "Módulo"
 msgid "Modules"
 msgid "Modules"
 msgstr "Módulos"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Directiva multilínea"
 msgstr "Directiva multilínea"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx conf not include stream-enabled"
 msgstr "La configuración de Nginx no incluye 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "El directorio de configuración de Nginx no está establecido"
 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"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI ya está instalado"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "La configuración de Nginx UI ha sido restaurada"
 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/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -3259,6 +3439,10 @@ msgstr "Las contraseñas no coinciden"
 msgid "Path"
 msgid "Path"
 msgstr "Ruta"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 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}"
 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"
 msgid "Payload resource is nil"
 msgstr "El recurso de carga útil es nulo"
 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Realizar"
 msgstr "Realizar"
@@ -3338,6 +3527,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgid "Please fill all fields correctly"
 msgstr "Por favor, complete todos los campos correctamente"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "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!"
 msgid "Please input your username!"
 msgstr "¡Por favor ingrese su nombre de usuario!"
 msgstr "¡Por favor ingrese su nombre de usuario!"
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Por favor, inicie sesión."
 msgstr "Por favor, inicie sesión."
 
 
@@ -3416,7 +3609,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Por favor, resuelva todos los problemas antes de proceder con la instalación"
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 "Por favor, guarde este token de seguridad, lo necesitará para la "
 "Por favor, guarde este token de seguridad, lo necesitará para la "
@@ -3889,6 +4082,109 @@ msgstr "Modo de ejecución"
 msgid "Running"
 msgid "Running"
 msgstr "Corriendo"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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 "
 "Escanee el código QR con su teléfono móvil para agregar la cuenta a la "
 "aplicación."
 "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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3993,7 +4297,7 @@ msgstr "Configuración de seguridad"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Token de seguridad"
 msgstr "Token de seguridad"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Información del token de seguridad"
 msgstr "Información del token de seguridad"
 
 
@@ -4256,6 +4560,29 @@ msgstr "Detenido"
 msgid "Storage"
 msgid "Storage"
 msgstr "Almacenamiento"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "La transmisión está habilitada"
 msgstr "La transmisión está habilitada"
@@ -4280,10 +4607,17 @@ msgstr "El directorio streams-enabled no existe"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "Puerto de estado stub"
 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"
 msgid "Success"
 msgstr "Éxito"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4400,7 +4734,7 @@ msgstr "Sincronización"
 msgid "System"
 msgid "System"
 msgstr "Sistema"
 msgstr "Sistema"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "Copia de seguridad del sistema"
 msgstr "Copia de seguridad del sistema"
 
 
@@ -4417,7 +4751,6 @@ msgid "System Restore"
 msgstr "Restauración del sistema"
 msgstr "Restauración del sistema"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "Sistema restaurado correctamente."
 msgstr "Sistema restaurado correctamente."
 
 
@@ -4441,6 +4774,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "Comando de inicio de terminal"
 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
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 "
 "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."
 "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 ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4661,6 +4998,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Acelerador"
 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/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4781,6 +5122,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Basura"
 msgstr "Basura"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Martes"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Se requiere autenticación de dos factores"
 msgstr "Se requiere autenticación de dos factores"
@@ -4797,6 +5142,10 @@ msgstr "Tipo"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Desconocido"
 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
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Actualizar contraseña"
 msgstr "Actualizar contraseña"
@@ -4809,6 +5158,7 @@ msgstr "Actualizar perfil"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Actualización exitosa"
 msgstr "Actualización exitosa"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4821,7 +5171,7 @@ msgstr "Actualización exitosa"
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Actualizado a"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4951,10 +5301,10 @@ msgstr "Visto"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Procesos de espera"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Advertencia"
 msgstr "Advertencia"
 
 
@@ -4997,6 +5347,18 @@ msgstr "La configuración de WebAuthn no está configurada"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "Error de conexión WebSocket"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5044,7 +5406,7 @@ msgstr "Procesos de trabajo"
 msgid "Workers"
 msgid "Workers"
 msgstr "Trabajadores"
 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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Espacio de trabajo"
 msgstr "Espacio de trabajo"
@@ -5131,6 +5493,9 @@ msgstr "Tus códigos antiguos ya no funcionarán."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Sus llaves de acceso"
 msgstr "Sus llaves de acceso"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Último error de copia de seguridad"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "Aplicar"
 #~ msgstr "Aplicar"
 
 
@@ -5155,9 +5520,6 @@ msgstr "Sus llaves de acceso"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "Ok"
 #~ msgstr "Ok"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Por favor, complete los campos requeridos"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "Recuperar"
 #~ msgstr "Recuperar"
 
 

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

@@ -112,7 +112,7 @@ msgstr "2fa"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Options 2FA"
 msgstr "Options 2FA"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "À propos"
 msgstr "À propos"
 
 
@@ -138,6 +138,7 @@ msgstr "Utilisateur ACME"
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -356,6 +357,11 @@ msgstr "Auto"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "auto = cœurs CPU"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Actualisation automatique"
 msgstr "Actualisation automatique"
@@ -397,7 +403,7 @@ msgstr "Retour au menu principal"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Retour à la liste"
 msgstr "Retour à la liste"
 
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgid "Backup"
 msgstr "Sauvegarde"
 msgstr "Sauvegarde"
 
 
@@ -411,10 +417,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr "Fichier de sauvegarde introuvable : {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"
 msgid "Backup has been downloaded successfully"
 msgstr "La sauvegarde a été téléchargée avec succès"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Minutes seuil de bannissement"
 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"
 msgid "Block is nil"
 msgstr "Le bloc est nul"
 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "Build avec"
 msgstr "Build avec"
@@ -542,6 +586,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Annuler"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 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"
 msgstr "Impossible de modifier le mot de passe de l'utilisateur initial en mode démo"
@@ -987,12 +1039,12 @@ msgstr "Contenu"
 msgid "Copied"
 msgid "Copied"
 msgstr "Copié"
 msgstr "Copié"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "Copié !"
 msgstr "Copié !"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Copier"
 msgstr "Copier"
 
 
@@ -1030,7 +1082,7 @@ msgstr "Créer"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Créer un autre"
 msgstr "Créer un autre"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Créer une sauvegarde"
 msgstr "Créer une sauvegarde"
 
 
@@ -1042,7 +1094,7 @@ msgstr "Créer un fichier"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Créer un dossier"
 msgstr "Créer un dossier"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "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 "
 "paramètres de Nginx UI. Les fichiers de sauvegarde seront automatiquement "
 "téléchargés sur votre ordinateur."
 "téléchargés sur votre ordinateur."
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1075,6 +1128,10 @@ msgstr "Identifiant"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Identifiants"
 msgstr "Identifiants"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Expression Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "Le compte actuel a le TOTP d'activé."
 msgstr "Le compte actuel a le TOTP d'activé."
@@ -1108,6 +1165,15 @@ msgstr "Version actuelle"
 msgid "Custom"
 msgid "Custom"
 msgstr "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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "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 "
 "Personnalisez le nom du nœud local à afficher dans l'indicateur "
 "d'environnement."
 "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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Jours"
 msgstr "Jours"
@@ -1320,6 +1402,7 @@ msgstr "Échec de la désactivation du flux %{name} depuis %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Désactivation du flux %{name} depuis %{node} réussie"
 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:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1455,6 +1538,10 @@ msgstr "Duplication locale réussie"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "Dynamique"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Modifier"
 msgstr "Modifier"
@@ -1581,6 +1668,8 @@ msgstr "Activer TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "Activer 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1647,6 +1736,18 @@ msgstr "Erreur lors du traitement du contenu"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Chemin exécutable"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1673,6 +1774,11 @@ msgstr "Notification Externe"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Échec de l'obtention du certificat"
 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
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Échec de l'attachement à l'instance d'exécution : {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"
 msgid "Failed to create backup"
 msgstr "Échec de la création de la sauvegarde"
 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
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Échec de la création du fichier de sauvegarde : {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}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Échec de la création du répertoire de restauration : {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Échec de la création du lien symbolique : {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}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Échec de la vérification des hachages : {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
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Échec de l'écriture du fichier décrypté : {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}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Échec de l'écriture du fichier chiffré : {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Échec de l'écriture dans le tampon ZIP : {0}"
 msgstr "Échec de l'écriture dans le tampon ZIP : {0}"
@@ -2028,6 +2150,14 @@ msgstr "Code de formatage"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Formaté avec succès"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Certificat général"
 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"
 msgid "History"
 msgstr "Historique"
 msgstr "Historique"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Menu principal"
 msgstr "Menu principal"
 
 
@@ -2112,6 +2242,10 @@ msgstr "Menu principal"
 msgid "Host"
 msgid "Host"
 msgstr "Hôte"
 msgstr "Hôte"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Heure"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2296,6 +2430,10 @@ msgstr "Remplissage invalide dans les données décryptées"
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "Code de vérification ou code de récupération invalide"
 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
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Code de récupération invalide"
 msgstr "Code de récupération invalide"
@@ -2365,6 +2503,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Personnalisé"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "Dernière vérification le"
 msgstr "Dernière vérification le"
@@ -2458,10 +2604,17 @@ msgstr "Chargement des données..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "Emplacement"
 msgstr "Emplacement"
@@ -2661,6 +2814,10 @@ msgstr "Espace libre minimum"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "Espace libre minimum dans le répertoire de cache"
 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
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Minutes"
 msgstr "Minutes"
@@ -2694,11 +2851,24 @@ msgstr "Module"
 msgid "Modules"
 msgid "Modules"
 msgstr "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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Directive multiligne"
 msgstr "Directive multiligne"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx conf not include stream-enabled"
 msgstr "La configuration Nginx n'inclut pas 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "Le répertoire de configuration de Nginx n'est pas défini"
 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"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI est déjà installé"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "La configuration de Nginx UI a été restaurée"
 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/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -3256,6 +3436,10 @@ msgstr "Les mots de passe ne correspondent pas"
 msgid "Path"
 msgid "Path"
 msgstr "Chemin"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr ""
 msgstr ""
@@ -3266,6 +3450,11 @@ msgstr ""
 msgid "Payload resource is nil"
 msgid "Payload resource is nil"
 msgstr "La ressource de charge utile est nulle"
 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Exécuter"
 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"
 msgid "Please fill all fields correctly"
 msgstr "Veuillez remplir tous les champs correctement"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "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!"
 msgid "Please input your username!"
 msgstr "Veuillez saisir votre nom d'utilisateur !"
 msgstr "Veuillez saisir votre nom d'utilisateur !"
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Veuillez vous connecter."
 msgstr "Veuillez vous connecter."
 
 
@@ -3413,7 +3606,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Veuillez résoudre tous les problèmes avant de procéder à l'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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 "Veuillez enregistrer ce jeton de sécurité, vous en aurez besoin pour la "
 "Veuillez enregistrer ce jeton de sécurité, vous en aurez besoin pour la "
@@ -3888,6 +4081,109 @@ msgstr "Mode d'exécution"
 msgid "Running"
 msgid "Running"
 msgstr "En cours d'éxécution"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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 à "
 "Scannez le code QR avec votre téléphone portable pour ajouter le compte à "
 "l'application."
 "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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3992,7 +4296,7 @@ msgstr "Paramètres de sécurité"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Jeton de sécurité"
 msgstr "Jeton de sécurité"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Informations sur le jeton de sécurité"
 msgstr "Informations sur le jeton de sécurité"
 
 
@@ -4255,6 +4559,29 @@ msgstr "Arrêté"
 msgid "Storage"
 msgid "Storage"
 msgstr "Stockage"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "Le flux est activé"
 msgstr "Le flux est activé"
@@ -4279,10 +4606,17 @@ msgstr "Le répertoire streams-enabled n'existe pas"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "Port d'état stub"
 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"
 msgid "Success"
 msgstr "Succès"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4401,7 +4735,7 @@ msgstr "Synchronisation"
 msgid "System"
 msgid "System"
 msgstr "Système"
 msgstr "Système"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "Sauvegarde du système"
 msgstr "Sauvegarde du système"
 
 
@@ -4418,7 +4752,6 @@ msgid "System Restore"
 msgstr "Restauration du système"
 msgstr "Restauration du système"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "Système restauré avec succès."
 msgstr "Système restauré avec succès."
 
 
@@ -4442,6 +4775,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "Commande de démarrage du terminal"
 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
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 "
 "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."
 "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 ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4668,6 +5005,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Limitation"
 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/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4789,6 +5130,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Corbeille"
 msgstr "Corbeille"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Mardi"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Authentification à deux facteurs requise"
 msgstr "Authentification à deux facteurs requise"
@@ -4805,6 +5150,10 @@ msgstr "Type"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Inconnu"
 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
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Mettre à jour le mot de passe"
 msgstr "Mettre à jour le mot de passe"
@@ -4817,6 +5166,7 @@ msgstr "Mettre à jour le profil"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Mise à jour réussie"
 msgstr "Mise à jour réussie"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4829,7 +5179,7 @@ msgstr "Mise à jour réussie"
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Mis à jour le"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4959,10 +5309,10 @@ msgstr "Vu"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Processus d'attente"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Avertissement"
 msgstr "Avertissement"
 
 
@@ -5005,6 +5355,18 @@ msgstr "Les paramètres WebAuthn ne sont pas configurés"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "Erreur de connexion WebSocket"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5055,7 +5417,7 @@ msgstr "Processus de travail"
 msgid "Workers"
 msgid "Workers"
 msgstr "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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Espace de travail"
 msgstr "Espace de travail"
@@ -5142,6 +5504,9 @@ msgstr "Vos anciens codes ne fonctionneront plus."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Vos clés d'accès"
 msgstr "Vos clés d'accès"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Dernière erreur de sauvegarde"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "Appliquer"
 #~ msgstr "Appliquer"
 
 
@@ -5166,9 +5531,6 @@ msgstr "Vos clés d'accès"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "Ok"
 #~ msgstr "Ok"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Veuillez remplir les champs obligatoires"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "Récupérer"
 #~ msgstr "Récupérer"
 
 
@@ -5232,10 +5594,6 @@ msgstr "Vos clés d'accès"
 #~ msgid "Nginx Conf Include Conf.d"
 #~ msgid "Nginx Conf Include Conf.d"
 #~ msgstr "Commande de démarrage du terminal"
 #~ msgstr "Commande de démarrage du terminal"
 
 
-#, fuzzy
-#~ msgid "Sites Directory"
-#~ msgstr "Directive"
-
 #~ msgid "Format error %{msg}"
 #~ msgid "Format error %{msg}"
 #~ msgstr "Erreur de format %{msg}"
 #~ msgstr "Erreur de format %{msg}"
 
 

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

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

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

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

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

@@ -94,7 +94,7 @@ msgstr ""
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr ""
 msgstr ""
 
 
@@ -121,6 +121,7 @@ msgstr ""
 msgid "Action"
 msgid "Action"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -338,6 +339,11 @@ msgstr ""
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr ""
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr ""
 msgstr ""
@@ -381,7 +387,8 @@ msgstr ""
 msgid "Back to List"
 msgid "Back to List"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11
+#: src/routes/modules/backup.ts:19
 msgid "Backup"
 msgid "Backup"
 msgstr ""
 msgstr ""
 
 
@@ -393,10 +400,38 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr ""
 msgstr ""
 
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgid "Backup has been downloaded successfully"
 msgstr ""
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr ""
 msgstr ""
@@ -452,6 +487,14 @@ msgstr ""
 msgid "Block is nil"
 msgid "Block is nil"
 msgstr ""
 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr ""
 msgstr ""
@@ -519,6 +562,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr ""
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgid "Cannot change initial user password in demo mode"
 msgstr ""
 msgstr ""
@@ -897,12 +948,12 @@ msgstr ""
 msgid "Copied"
 msgid "Copied"
 msgstr ""
 msgstr ""
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr ""
 msgstr ""
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr ""
 msgstr ""
 
 
@@ -938,7 +989,7 @@ msgstr ""
 msgid "Create Another"
 msgid "Create Another"
 msgstr ""
 msgstr ""
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr ""
 msgstr ""
 
 
@@ -951,10 +1002,11 @@ msgstr ""
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr ""
 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."
 msgid "Create system backups including Nginx configuration and Nginx UI settings. Backup files will be automatically downloaded to your computer."
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -979,6 +1031,10 @@ msgstr ""
 msgid "Credentials"
 msgid "Credentials"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr ""
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr ""
 msgstr ""
@@ -1012,10 +1068,27 @@ msgstr ""
 msgid "Custom"
 msgid "Custom"
 msgstr ""
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid "Customize the name of local node to be displayed in the environment indicator."
 msgid "Customize the name of local node to be displayed in the environment indicator."
 msgstr ""
 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/routes/modules/dashboard.ts:10
 #: src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161
 #: src/views/config/ConfigEditor.vue:161
@@ -1023,6 +1096,14 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr ""
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr ""
 msgstr ""
@@ -1226,6 +1307,7 @@ msgstr ""
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1358,6 +1440,10 @@ msgstr ""
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:197
+msgid "e.g., 0 0 * * * (daily at midnight)"
+msgstr ""
+
 #: src/language/curd.ts:8
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr ""
 msgstr ""
@@ -1487,6 +1573,8 @@ msgstr ""
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr ""
 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1555,6 +1643,18 @@ msgstr ""
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr ""
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1581,6 +1681,11 @@ msgstr ""
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr ""
+
 #: src/constants/errors/docker.ts:4
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr ""
 msgstr ""
@@ -1633,6 +1738,10 @@ msgstr ""
 msgid "Failed to create backup"
 msgid "Failed to create backup"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:12
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr ""
 msgstr ""
@@ -1657,6 +1766,10 @@ msgstr ""
 msgid "Failed to create restore directory: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/errors/backup.ts:78
+msgid "Failed to create storage directory {0}: {1}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:50
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr ""
 msgstr ""
@@ -1869,6 +1982,10 @@ msgstr ""
 msgid "Failed to verify hashes: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:55
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr ""
 msgstr ""
@@ -1877,6 +1994,10 @@ msgstr ""
 msgid "Failed to write encrypted file: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/errors/backup.ts:67
+msgid "Failed to write security key file: {0}"
+msgstr ""
+
 #: src/constants/errors/backup.ts:33
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr ""
 msgstr ""
@@ -1931,6 +2052,14 @@ msgstr ""
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr ""
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr ""
 msgstr ""
@@ -2007,7 +2136,7 @@ msgstr ""
 msgid "History"
 msgid "History"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr ""
 msgstr ""
 
 
@@ -2015,6 +2144,10 @@ msgstr ""
 msgid "Host"
 msgid "Host"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr ""
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr ""
 msgstr ""
@@ -2177,6 +2310,10 @@ msgstr ""
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr ""
+
 #: src/constants/errors/user.ts:5
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr ""
 msgstr ""
@@ -2242,6 +2379,14 @@ msgstr ""
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr ""
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr ""
 msgstr ""
@@ -2335,10 +2480,17 @@ msgstr ""
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:58
+msgid "Local path (e.g., /var/backups)"
+msgstr ""
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr ""
 msgstr ""
@@ -2522,6 +2674,10 @@ msgstr ""
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr ""
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
@@ -2556,11 +2712,24 @@ msgstr ""
 msgid "Modules"
 msgid "Modules"
 msgstr ""
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr ""
 msgstr ""
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2662,6 +2831,11 @@ msgstr ""
 msgid "Nginx conf not include stream-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr ""
 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr ""
 msgstr ""
@@ -2802,6 +2976,11 @@ msgstr ""
 msgid "Nginx UI already installed"
 msgid "Nginx UI already installed"
 msgstr ""
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr ""
 msgstr ""
@@ -2971,6 +3150,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110
 #: src/components/Notification/Notification.vue:110
 #: src/language/curd.ts:15
 #: src/language/curd.ts:15
+#: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -3109,6 +3288,10 @@ msgstr ""
 msgid "Path"
 msgid "Path"
 msgstr ""
 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/cert.ts:7
 #: src/constants/errors/config.ts:2
 #: src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgid "Path: {0} is not under the nginx conf dir: {1}"
@@ -3118,6 +3301,11 @@ msgstr ""
 msgid "Payload resource is nil"
 msgid "Payload resource is nil"
 msgstr ""
 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr ""
 msgstr ""
@@ -3179,6 +3367,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgid "Please fill all fields correctly"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:21
+msgid "Please fill in required S3 configuration fields"
+msgstr ""
+
 #: src/views/certificate/DNSCredential.vue:52
 #: src/views/certificate/DNSCredential.vue:52
 msgid "Please fill in the API authentication credentials provided by your DNS provider."
 msgid "Please fill in the API authentication credentials provided by your DNS provider."
 msgstr ""
 msgstr ""
@@ -3223,8 +3415,8 @@ msgstr ""
 msgid "Please input your username!"
 msgid "Please input your username!"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr ""
 msgstr ""
 
 
@@ -3236,7 +3428,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr ""
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 
 
@@ -3694,6 +3886,109 @@ msgstr ""
 msgid "Running"
 msgid "Running"
 msgstr ""
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18
 #: 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."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr ""
 msgstr ""
@@ -3800,7 +4103,7 @@ msgstr ""
 msgid "Security Token"
 msgid "Security Token"
 msgstr ""
 msgstr ""
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr ""
 msgstr ""
 
 
@@ -4049,6 +4352,29 @@ msgstr ""
 msgid "Storage"
 msgid "Storage"
 msgstr ""
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr ""
 msgstr ""
@@ -4074,10 +4400,17 @@ msgid "Stub Status Port"
 msgstr ""
 msgstr ""
 
 
 #: src/constants/index.ts:25
 #: 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
 #: src/views/notification/notificationColumns.tsx:35
 msgid "Success"
 msgid "Success"
 msgstr ""
 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
 #: 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"
 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 ""
 msgstr ""
@@ -4183,7 +4516,7 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr ""
 msgstr ""
 
 
@@ -4200,7 +4533,6 @@ msgid "System Restore"
 msgstr ""
 msgstr ""
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr ""
 msgstr ""
 
 
@@ -4225,6 +4557,10 @@ msgstr ""
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr ""
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 #: 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."
 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 ""
 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."
 msgid "This operation will only remove the certificate from the database. The certificate files on the file system will not be deleted."
 msgstr ""
 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."
 msgid "This token will only be shown once and cannot be retrieved later. Please make sure to save it in a secure location."
 msgstr ""
 msgstr ""
 
 
@@ -4380,6 +4716,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr ""
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4466,6 +4806,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr ""
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr ""
 msgstr ""
@@ -4482,6 +4826,10 @@ msgstr ""
 msgid "Unknown"
 msgid "Unknown"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr ""
+
 #: src/views/user/UserProfile.vue:218
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr ""
 msgstr ""
@@ -4494,6 +4842,7 @@ msgstr ""
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr ""
 msgstr ""
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35
 #: src/views/config/configColumns.tsx:35
@@ -4508,7 +4857,7 @@ msgstr ""
 msgid "Updated at"
 msgid "Updated at"
 msgstr ""
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154
 #: src/views/system/Upgrade.vue:154
@@ -4643,10 +4992,10 @@ msgid "Waiting processes"
 msgstr ""
 msgstr ""
 
 
 #: src/constants/index.ts:23
 #: src/constants/index.ts:23
+#: src/views/backup/components/BackupCreator.vue:138
 #: src/views/config/InspectConfig.vue:33
 #: src/views/config/InspectConfig.vue:33
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr ""
 msgstr ""
 
 
@@ -4675,6 +5024,18 @@ msgstr ""
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr ""
 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
 #: 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."
 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 ""
 msgstr ""
@@ -4708,7 +5069,7 @@ msgid "Workers"
 msgstr ""
 msgstr ""
 
 
 #: src/layouts/HeaderLayout.vue:62
 #: src/layouts/HeaderLayout.vue:62
-#: src/routes/index.ts:56
+#: src/routes/index.ts:57
 #: src/views/workspace/WorkSpace.vue:52
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr ""
 msgstr ""

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

@@ -108,7 +108,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Definições 2FA"
 msgstr "Definições 2FA"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "Sobre"
 msgstr "Sobre"
 
 
@@ -134,6 +134,7 @@ msgstr "Utilizador ACME"
 msgid "Action"
 msgid "Action"
 msgstr "Acção"
 msgstr "Acção"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -352,6 +353,11 @@ msgstr "Automático"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "auto = núcleos da CPU"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Actualizar Automaticamente"
 msgstr "Actualizar Automaticamente"
@@ -393,7 +399,7 @@ msgstr "Voltar ao Início"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Voltar à lista"
 msgstr "Voltar à lista"
 
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgid "Backup"
 msgstr "Cópia de segurança"
 msgstr "Cópia de segurança"
 
 
@@ -407,10 +413,38 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr "Ficheiro de cópia de segurança não encontrado: {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"
 msgid "Backup has been downloaded successfully"
 msgstr "O backup foi descarregado com sucesso"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Minutos Limite para Banir"
 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"
 msgid "Block is nil"
 msgstr "O bloco é nulo"
 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "Build com"
 msgstr "Build com"
@@ -537,6 +579,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Cancelar"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 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"
 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"
 msgid "Copied"
 msgstr "Copiado"
 msgstr "Copiado"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "Copiado!"
 msgstr "Copiado!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Copiar"
 msgstr "Copiar"
 
 
@@ -1025,7 +1075,7 @@ msgstr "Criar"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Criar Outro"
 msgstr "Criar Outro"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Criar cópia de segurança"
 msgstr "Criar cópia de segurança"
 
 
@@ -1037,7 +1087,7 @@ msgstr "Criar Ficheiro"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Criar Pasta"
 msgstr "Criar Pasta"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "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 "
 "definições do Nginx UI. Os ficheiros de backup serão transferidos "
 "automaticamente para o seu computador."
 "automaticamente para o seu computador."
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1070,6 +1121,10 @@ msgstr "Credencial"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Credenciais"
 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
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "A conta atual tem o TOTP ativado."
 msgstr "A conta atual tem o TOTP ativado."
@@ -1103,17 +1158,42 @@ msgstr "Versão Actual"
 msgid "Custom"
 msgid "Custom"
 msgstr "Personalizado"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 "indicator."
 msgstr "Personalize o nome do nó local a ser exibido no indicador de ambiente."
 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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Painel"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Dias"
 msgstr "Dias"
@@ -1313,6 +1393,7 @@ msgstr "Falha ao desativar o fluxo %{name} de %{node}"
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Desativar o fluxo %{name} de %{node} com sucesso"
 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:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1448,6 +1529,10 @@ msgstr "Duplicado para local com sucesso"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "Dinâmico"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Editar"
 msgstr "Editar"
@@ -1574,6 +1659,8 @@ msgstr "Activar TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "Ativar 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1640,6 +1727,18 @@ msgstr "Erro ao processar o conteúdo"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Caminho Executável"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1666,6 +1765,11 @@ msgstr "Notificação Externa"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Obtenção de Certificado Falhou"
 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
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Falha ao anexar à instância de execução: {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"
 msgid "Failed to create backup"
 msgstr "Falha ao criar cópia de segurança"
 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
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Falha ao criar o ficheiro de backup: {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}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Falha ao criar o diretório de restauro: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Falha ao criar ligação simbólica: {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}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Falha ao verificar 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
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Falha ao escrever o ficheiro desencriptado: {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}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Falha ao escrever o ficheiro encriptado: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Falha ao escrever no buffer ZIP: {0}"
 msgstr "Falha ao escrever no buffer ZIP: {0}"
@@ -2019,6 +2139,14 @@ msgstr "Formatar Código"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Formatado com Sucesso"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Certificado Geral"
 msgstr "Certificado Geral"
@@ -2095,7 +2223,7 @@ msgstr "Um valor mais alto significa uma melhor reutilização da conexão"
 msgid "History"
 msgid "History"
 msgstr "Histórico"
 msgstr "Histórico"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Início"
 msgstr "Início"
 
 
@@ -2103,6 +2231,10 @@ msgstr "Início"
 msgid "Host"
 msgid "Host"
 msgstr "Host"
 msgstr "Host"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Hora"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2285,6 +2417,10 @@ msgstr "Preenchimento inválido nos dados desencriptados"
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "Passcode ou código de recuperação inválido"
 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
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Código de recuperação inválido"
 msgstr "Código de recuperação inválido"
@@ -2354,6 +2490,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Personalizado"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "Última verificação em"
 msgstr "Última verificação em"
@@ -2447,10 +2591,17 @@ msgstr "A carregar dados..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "Localização"
 msgstr "Localização"
@@ -2649,6 +2800,10 @@ msgstr "Espaço livre mínimo"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "Espaço livre mínimo no diretório de cache"
 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
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Minutos"
 msgstr "Minutos"
@@ -2682,11 +2837,24 @@ msgstr "Módulo"
 msgid "Modules"
 msgid "Modules"
 msgstr "Módulos"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Diretiva Multilinha"
 msgstr "Diretiva Multilinha"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx conf not include stream-enabled"
 msgstr "A configuração do Nginx não inclui 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "O diretório de configuração do Nginx não está definido"
 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"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI já está instalado"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "A configuração do Nginx UI foi restaurada"
 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/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -3244,6 +3422,10 @@ msgstr "As palavras-passe não coincidem"
 msgid "Path"
 msgid "Path"
 msgstr "Caminho"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 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}"
 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"
 msgid "Payload resource is nil"
 msgstr "O recurso de carga útil é nulo"
 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Realizar"
 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"
 msgid "Please fill all fields correctly"
 msgstr "Por favor, preencha todos os campos corretamente"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "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!"
 msgid "Please input your username!"
 msgstr "Por favor introduza o seu nome de utilizador!"
 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/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Por favor, faça login."
 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"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Por favor, resolva todos os problemas antes de prosseguir com a instalação"
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 "Por favor, guarde este token de segurança, vai precisar dele para a "
 "Por favor, guarde este token de segurança, vai precisar dele para a "
@@ -3870,6 +4061,109 @@ msgstr "Modo de Execução"
 msgid "Running"
 msgid "Running"
 msgstr "Executando"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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 à "
 "Digitalize o código QR com o seu telemóvel para adicionar a conta à "
 "aplicação."
 "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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3974,7 +4276,7 @@ msgstr "Configurações de segurança"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Token de segurança"
 msgstr "Token de segurança"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Informações do token de segurança"
 msgstr "Informações do token de segurança"
 
 
@@ -4237,6 +4539,29 @@ msgstr "Parado"
 msgid "Storage"
 msgid "Storage"
 msgstr "Armazenamento"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "O fluxo está ativado"
 msgstr "O fluxo está ativado"
@@ -4261,10 +4586,17 @@ msgstr "O diretório streams-enabled não existe"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "Porta de estado stub"
 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"
 msgid "Success"
 msgstr "Sucesso"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4380,7 +4712,7 @@ msgstr "Sincronização"
 msgid "System"
 msgid "System"
 msgstr "Sistema"
 msgstr "Sistema"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "Cópia de segurança do sistema"
 msgstr "Cópia de segurança do sistema"
 
 
@@ -4397,7 +4729,6 @@ msgid "System Restore"
 msgstr "Restauro do sistema"
 msgstr "Restauro do sistema"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "Sistema restaurado com sucesso."
 msgstr "Sistema restaurado com sucesso."
 
 
@@ -4421,6 +4752,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "Comando de Inicialização do Terminal"
 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
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 "
 "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."
 "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 ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4640,6 +4975,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Limitação"
 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/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4758,6 +5097,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Lixo"
 msgstr "Lixo"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Terça-feira"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Autenticação de dois fatores necessária"
 msgstr "Autenticação de dois fatores necessária"
@@ -4774,6 +5117,10 @@ msgstr "Tipo"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Desconhecido"
 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
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Atualizar senha"
 msgstr "Atualizar senha"
@@ -4786,6 +5133,7 @@ msgstr "Atualizar perfil"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Atualização bem-sucedida"
 msgstr "Atualização bem-sucedida"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4798,7 +5146,7 @@ msgstr "Atualização bem-sucedida"
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Actualizado em"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4928,10 +5276,10 @@ msgstr "Visualizado"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Processos em espera"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Aviso"
 msgstr "Aviso"
 
 
@@ -4974,6 +5322,18 @@ msgstr "As configurações do WebAuthn não estão configuradas"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "Erro de conexão WebSocket"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5020,7 +5380,7 @@ msgstr "Processos de trabalho"
 msgid "Workers"
 msgid "Workers"
 msgstr "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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Espaço de Trabalho"
 msgstr "Espaço de Trabalho"
@@ -5107,6 +5467,9 @@ msgstr "Os seus códigos antigos não funcionarão mais."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "As suas chaves de acesso"
 msgstr "As suas chaves de acesso"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Último erro de backup"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "Aplicar"
 #~ msgstr "Aplicar"
 
 
@@ -5131,9 +5494,6 @@ msgstr "As suas chaves de acesso"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "Ok"
 #~ msgstr "Ok"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Por favor preencha os campos obrigatórios"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "Recuperar"
 #~ msgstr "Recuperar"
 
 

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

@@ -112,7 +112,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Настройки 2FA"
 msgstr "Настройки 2FA"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "О проекте"
 msgstr "О проекте"
 
 
@@ -138,6 +138,7 @@ msgstr "Пользователь ACME"
 msgid "Action"
 msgid "Action"
 msgstr "Действие"
 msgstr "Действие"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -354,6 +355,11 @@ msgstr "Авто"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "Auto = ядра процессора"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Автообновление"
 msgstr "Автообновление"
@@ -395,7 +401,7 @@ msgstr "Вернуться на главную"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Вернуться к списку"
 msgstr "Вернуться к списку"
 
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgid "Backup"
 msgstr "Резервная копия"
 msgstr "Резервная копия"
 
 
@@ -409,10 +415,40 @@ msgstr ""
 msgid "Backup file not found: {0}"
 msgid "Backup file not found: {0}"
 msgstr "Файл резервной копии не найден: {0}"
 msgstr "Файл резервной копии не найден: {0}"
 
 
-#: src/views/system/Backup/BackupCreator.vue:42
+#: src/views/backup/components/BackupCreator.vue:42
 msgid "Backup has been downloaded successfully"
 msgid "Backup has been downloaded successfully"
 msgstr "Резервная копия успешно загружена"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Порог блокировки в минутах"
 msgstr "Порог блокировки в минутах"
@@ -468,6 +504,14 @@ msgstr "Ниже приведены выбранные элементы, кот
 msgid "Block is nil"
 msgid "Block is nil"
 msgstr "Блок равен 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "Собрать с"
 msgstr "Собрать с"
@@ -541,6 +585,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Отмена"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgid "Cannot change initial user password in demo mode"
 msgstr "Невозможно изменить пароль начального пользователя в демо-режиме"
 msgstr "Невозможно изменить пароль начального пользователя в демо-режиме"
@@ -983,12 +1035,12 @@ msgstr "Содержание"
 msgid "Copied"
 msgid "Copied"
 msgstr "Скопировано"
 msgstr "Скопировано"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "Скопировано!"
 msgstr "Скопировано!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Копировать"
 msgstr "Копировать"
 
 
@@ -1026,7 +1078,7 @@ msgstr "Создать"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Создать еще"
 msgstr "Создать еще"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Создать резервную копию"
 msgstr "Создать резервную копию"
 
 
@@ -1038,7 +1090,7 @@ msgstr "Создать файл"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Создать папку"
 msgstr "Создать папку"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "Backup files will be automatically downloaded to your computer."
@@ -1047,6 +1099,7 @@ msgstr ""
 "Nginx UI. Файлы резервных копий будут автоматически загружены на ваш "
 "Nginx UI. Файлы резервных копий будут автоматически загружены на ваш "
 "компьютер."
 "компьютер."
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:227
 #: src/views/environments/group/columns.ts:29
 #: src/views/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1071,6 +1124,10 @@ msgstr "Учетные данные"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Учетные данные"
 msgstr "Учетные данные"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:194
+msgid "Cron Expression"
+msgstr "Выражение Cron"
+
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "Текущая учетная запись имеет включенную TOTP."
 msgstr "Текущая учетная запись имеет включенную TOTP."
@@ -1104,17 +1161,42 @@ msgstr "Текущяя версия"
 msgid "Custom"
 msgid "Custom"
 msgstr "Пользовательский"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 "indicator."
 msgstr "Настройте имя локального узла для отображения в индикаторе среды."
 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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Доска"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Дни"
 msgstr "Дни"
@@ -1312,6 +1394,7 @@ msgstr "Не удалось отключить поток %{name} с узла %{
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Поток %{name} отключен от %{node} успешно"
 msgstr "Поток %{name} отключен от %{node} успешно"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:173
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1447,6 +1530,10 @@ msgstr "Успешно дублировано на локальный"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "Динамический"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Редактировать"
 msgstr "Редактировать"
@@ -1573,6 +1660,8 @@ msgstr "Включить TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "Включить 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1639,6 +1728,18 @@ msgstr "Ошибка обработки содержимого"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Исполняемый путь"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1665,6 +1766,11 @@ msgstr "Внешнее уведомление"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Не удалось получить сертификат"
 msgstr "Не удалось получить сертификат"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:194
+#: src/views/backup/AutoBackup/AutoBackup.vue:219
+msgid "Failed"
+msgstr "Не удалось"
+
 #: src/constants/errors/docker.ts:4
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Не удалось подключиться к экземпляру выполнения: {0}"
 msgstr "Не удалось подключиться к экземпляру выполнения: {0}"
@@ -1717,6 +1823,10 @@ msgstr "Не удалось скопировать каталог конфигу
 msgid "Failed to create backup"
 msgid "Failed to create backup"
 msgstr "Не удалось создать резервную копию"
 msgstr "Не удалось создать резервную копию"
 
 
+#: src/constants/errors/backup.ts:65
+msgid "Failed to create backup directory: {0}"
+msgstr "Не удалось создать каталог резервной копии: {0}"
+
 #: src/constants/errors/backup.ts:12
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Не удалось создать файл резервной копии: {0}"
 msgstr "Не удалось создать файл резервной копии: {0}"
@@ -1741,6 +1851,10 @@ msgstr "Не удалось создать родительский катало
 msgid "Failed to create restore directory: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Не удалось создать каталог восстановления: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Не удалось создать символическую ссылку: {0}"
 msgstr "Не удалось создать символическую ссылку: {0}"
@@ -1953,6 +2067,10 @@ msgstr "Не удалось запустить временный контейн
 msgid "Failed to verify hashes: {0}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Не удалось проверить хеши: {0}"
 msgstr "Не удалось проверить хеши: {0}"
 
 
+#: src/constants/errors/backup.ts:66
+msgid "Failed to write backup file: {0}"
+msgstr "Не удалось записать файл резервной копии: {0}"
+
 #: src/constants/errors/backup.ts:55
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Ошибка записи расшифрованного файла: {0}"
 msgstr "Ошибка записи расшифрованного файла: {0}"
@@ -1961,6 +2079,10 @@ msgstr "Ошибка записи расшифрованного файла: {0}
 msgid "Failed to write encrypted file: {0}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Ошибка записи зашифрованного файла: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Ошибка записи в ZIP-буфер: {0}"
 msgstr "Ошибка записи в ZIP-буфер: {0}"
@@ -2018,6 +2140,14 @@ msgstr "Форматировать код"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Форматирование успешно"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Общий сертификат"
 msgstr "Общий сертификат"
@@ -2094,7 +2224,7 @@ msgstr "Более высокое значение означает лучшее
 msgid "History"
 msgid "History"
 msgstr "История"
 msgstr "История"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Главная"
 msgstr "Главная"
 
 
@@ -2102,6 +2232,10 @@ msgstr "Главная"
 msgid "Host"
 msgid "Host"
 msgstr "Хост"
 msgstr "Хост"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Час"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2282,6 +2416,10 @@ msgstr "Некорректное заполнение в расшифрован
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "Неверный пароль или код восстановления"
 msgstr "Неверный пароль или код восстановления"
 
 
+#: src/constants/errors/backup.ts:73
+msgid "Invalid path: {0}"
+msgstr "Неверный путь: {0}"
+
 #: src/constants/errors/user.ts:5
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Неверный код восстановления"
 msgstr "Неверный код восстановления"
@@ -2351,6 +2489,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Пользовательский"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "Последняя проверка в"
 msgstr "Последняя проверка в"
@@ -2444,10 +2590,17 @@ msgstr "Загрузка данных..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "Локальный"
 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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "Локация"
 msgstr "Локация"
@@ -2646,6 +2799,10 @@ msgstr "Минимальное свободное пространство"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "Минимальное свободное место в кэш-директории"
 msgstr "Минимальное свободное место в кэш-директории"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:169
+msgid "Minute"
+msgstr "Минута"
+
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Минуты"
 msgstr "Минуты"
@@ -2679,11 +2836,24 @@ msgstr "Модуль"
 msgid "Modules"
 msgid "Modules"
 msgstr "Модули"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Многострочная директива"
 msgstr "Многострочная директива"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -2783,6 +2953,11 @@ msgstr "Конфигурация Nginx не включает sites-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Конфигурация Nginx не включает 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "Каталог конфигурации Nginx не задан"
 msgstr "Каталог конфигурации Nginx не задан"
@@ -2920,6 +3095,11 @@ msgstr "Теоретическая максимальная производит
 msgid "Nginx UI already installed"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI уже установлен"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "Конфигурация Nginx UI была восстановлена"
 msgstr "Конфигурация Nginx UI была восстановлена"
@@ -3099,6 +3279,7 @@ msgstr "Оффлайн"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "ОК"
 msgstr "ОК"
 
 
@@ -3241,6 +3421,10 @@ msgstr "Пароли не совпадают"
 msgid "Path"
 msgid "Path"
 msgstr "Путь"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Путь: {0} не находится в каталоге конфигурации nginx: {1}"
 msgstr "Путь: {0} не находится в каталоге конфигурации nginx: {1}"
@@ -3249,6 +3433,11 @@ msgstr "Путь: {0} не находится в каталоге конфигу
 msgid "Payload resource is nil"
 msgid "Payload resource is nil"
 msgstr "Ресурс полезной нагрузки равен 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Выполнить"
 msgstr "Выполнить"
@@ -3320,6 +3509,10 @@ msgstr ""
 msgid "Please fill all fields correctly"
 msgid "Please fill all fields correctly"
 msgstr "Пожалуйста, заполните все поля правильно"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "Please fill in the API authentication credentials provided by your DNS "
@@ -3383,8 +3576,8 @@ msgstr "Введите ваш пароль!"
 msgid "Please input your username!"
 msgid "Please input your username!"
 msgstr "Введите ваше имя пользователя!"
 msgstr "Введите ваше имя пользователя!"
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Пожалуйста, войдите в систему."
 msgstr "Пожалуйста, войдите в систему."
 
 
@@ -3398,7 +3591,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Пожалуйста, устраните все проблемы перед продолжением установки"
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 "Пожалуйста, сохраните этот токен безопасности, он понадобится для "
 "Пожалуйста, сохраните этот токен безопасности, он понадобится для "
@@ -3870,6 +4063,109 @@ msgstr "Режим работы"
 msgid "Running"
 msgid "Running"
 msgstr "Выполняется"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
@@ -3949,6 +4245,14 @@ msgstr ""
 "Отсканируйте QR-код с помощью мобильного телефона, чтобы добавить учетную "
 "Отсканируйте 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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3974,7 +4278,7 @@ msgstr "Настройки безопасности"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Токен безопасности"
 msgstr "Токен безопасности"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Информация о токене безопасности"
 msgstr "Информация о токене безопасности"
 
 
@@ -4231,6 +4535,29 @@ msgstr "Остановлен"
 msgid "Storage"
 msgid "Storage"
 msgstr "Хранилище"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "Поток включен"
 msgstr "Поток включен"
@@ -4255,10 +4582,17 @@ msgstr "Каталог streams-enabled не существует"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "Порт состояния заглушки"
 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"
 msgid "Success"
 msgstr "Успех"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4374,7 +4708,7 @@ msgstr "Синхронизация"
 msgid "System"
 msgid "System"
 msgstr "Система"
 msgstr "Система"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "Резервное копирование системы"
 msgstr "Резервное копирование системы"
 
 
@@ -4391,7 +4725,6 @@ msgid "System Restore"
 msgstr "Восстановление системы"
 msgstr "Восстановление системы"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "Система успешно восстановлена."
 msgstr "Система успешно восстановлена."
 
 
@@ -4415,6 +4748,10 @@ msgstr "Терминал"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "Терминальная команда запуска"
 msgstr "Терминальная команда запуска"
 
 
+#: src/views/backup/AutoBackup/components/StorageConfigEditor.vue:139
+msgid "Test S3 Connection"
+msgstr "Проверить подключение S3"
+
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4635,6 +4972,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Ограничение"
 msgstr "Ограничение"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:30
+msgid "Thursday"
+msgstr "Четверг"
+
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4753,6 +5094,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Корзина"
 msgstr "Корзина"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Вторник"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Требуется двухфакторная аутентификация"
 msgstr "Требуется двухфакторная аутентификация"
@@ -4769,6 +5114,10 @@ msgstr "Тип"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Неизвестно"
 msgstr "Неизвестно"
 
 
+#: src/constants/errors/backup.ts:64
+msgid "Unsupported backup type: {0}"
+msgstr "Неподдерживаемый тип резервного копирования: {0}"
+
 #: src/views/user/UserProfile.vue:218
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Обновить пароль"
 msgstr "Обновить пароль"
@@ -4781,6 +5130,7 @@ msgstr "Обновить профиль"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Успешно обновлено"
 msgstr "Успешно обновлено"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
@@ -4793,7 +5143,7 @@ msgstr "Успешно обновлено"
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Обновлено в"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4923,10 +5273,10 @@ msgstr "Просмотрено"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Процессы ожидания"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Внимание"
 msgstr "Внимание"
 
 
@@ -4969,6 +5319,18 @@ msgstr "Настройки WebAuthn не настроены"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "Ошибка подключения WebSocket"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5015,7 +5377,7 @@ msgstr "Рабочие процессы"
 msgid "Workers"
 msgid "Workers"
 msgstr "Рабочие процессы"
 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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Рабочее пространство"
 msgstr "Рабочее пространство"
@@ -5101,6 +5463,9 @@ msgstr "Ваши старые коды больше не будут работа
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Ваши ключи доступа"
 msgstr "Ваши ключи доступа"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Ошибка последнего резервного копирования"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "Применить"
 #~ msgstr "Применить"
 
 
@@ -5125,9 +5490,6 @@ msgstr "Ваши ключи доступа"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "Ок"
 #~ msgstr "Ок"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Пожалуйста, заполните обязательные поля"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "Восстановить"
 #~ 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"
 msgid "2FA Settings"
 msgstr "2FA Ayarları"
 msgstr "2FA Ayarları"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "Hakkında"
 msgstr "Hakkında"
 
 
@@ -134,6 +134,7 @@ msgstr "ACME Kullanıcısı"
 msgid "Action"
 msgid "Action"
 msgstr "Eylem"
 msgstr "Eylem"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -350,6 +351,11 @@ msgstr "Otomobil"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "Auto = CPU Çekirdekleri"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Otomatik Yenileme"
 msgstr "Otomatik Yenileme"
@@ -391,7 +397,7 @@ msgstr "Ana Sayfaya Dön"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Listeye Dön"
 msgstr "Listeye Dön"
 
 
-#: src/routes/modules/system.ts:26
+#: src/routes/modules/backup.ts:11 src/routes/modules/backup.ts:19
 msgid "Backup"
 msgid "Backup"
 msgstr "Yedekleme"
 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}"
 msgid "Backup file not found: {0}"
 msgstr "Yedek dosya bulunamadı: {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"
 msgid "Backup has been downloaded successfully"
 msgstr "Yedek başarıyla indirildi"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Yasaklama Eşiği Süresi (Dakika)"
 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"
 msgid "Block is nil"
 msgstr "Blok nil değerinde"
 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "İle Oluşturuldu"
 msgstr "İle Oluşturuldu"
@@ -533,6 +575,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "İptal"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 msgid "Cannot change initial user password in demo mode"
 msgstr "Demo modunda başlangıç kullanıcı şifresi değiştirilemez"
 msgstr "Demo modunda başlangıç kullanıcı şifresi değiştirilemez"
@@ -978,12 +1028,12 @@ msgstr "İçerik"
 msgid "Copied"
 msgid "Copied"
 msgstr "Kopyalandı"
 msgstr "Kopyalandı"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "Kopyalandı!"
 msgstr "Kopyalandı!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Kopya"
 msgstr "Kopya"
 
 
@@ -1021,7 +1071,7 @@ msgstr "Oluştur"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Bir Başka Oluştur"
 msgstr "Bir Başka Oluştur"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Yedek Oluştur"
 msgstr "Yedek Oluştur"
 
 
@@ -1033,7 +1083,7 @@ msgstr "Dosya Oluştur"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Klasör Ekle"
 msgstr "Klasör Ekle"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "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 "
 "Nginx yapılandırması ve Nginx UI ayarlarını içeren sistem yedekleri "
 "oluşturun. Yedek dosyaları otomatik olarak bilgisayarınıza indirilecektir."
 "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/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1065,6 +1116,10 @@ msgstr "Kimlik bilgisi"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Kimlik bilgileri"
 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
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "Mevcut hesap için TOTP etkinleştirildi."
 msgstr "Mevcut hesap için TOTP etkinleştirildi."
@@ -1098,17 +1153,42 @@ msgstr "Mevcut sürüm"
 msgid "Custom"
 msgid "Custom"
 msgstr "Özelleştirilmiş"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 "indicator."
 msgstr "Ortam göstergesinde görüntülenecek yerel düğüm adını özelleştirin."
 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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Kontrol Paneli"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Günler"
 msgstr "Günler"
@@ -1308,6 +1388,7 @@ msgstr "%{node} üzerindeki %{name} akışı devre dışı bırakılamadı"
 msgid "Disable stream %{name} from %{node} successfully"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Akış %{name}, %{node} üzerinden başarıyla devre dışı bırakıldı"
 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:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1445,6 +1526,10 @@ msgstr "Başarıyla yerel kopya oluşturuldu"
 msgid "Dynamic"
 msgid "Dynamic"
 msgstr "Dinamik"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Düzenle"
 msgstr "Düzenle"
@@ -1571,6 +1656,8 @@ msgstr "TLS'yi Etkinleştir"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "TOTP'yi Etkinleştir"
 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1637,6 +1724,18 @@ msgstr "İçerik işlenirken hata"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Yürütülebilir Dosya Yolu"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1663,6 +1762,11 @@ msgstr "Harici Bildirim"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Sertifika alınamadı"
 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
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 msgid "Failed to attach to exec instance: {0}"
 msgstr "Yürütme örneğine bağlanılamadı: {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"
 msgid "Failed to create backup"
 msgstr "Yedek oluşturma başarısız oldu"
 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
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Yedek dosyası oluşturulamadı: {0}"
 msgstr "Yedek dosyası oluşturulamadı: {0}"
@@ -1739,6 +1847,10 @@ msgstr "Üst dizin oluşturulamadı: {0}"
 msgid "Failed to create restore directory: {0}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Geri yükleme dizini oluşturulamadı: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Sembolik bağlantı oluşturulamadı: {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}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Hash doğrulama başarısız: {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
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Şifresi çözülmüş dosya yazılamadı: {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}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Şifrelenmiş dosya yazma hatası: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "ZIP tamponuna yazma başarısız: {0}"
 msgstr "ZIP tamponuna yazma başarısız: {0}"
@@ -2016,6 +2136,14 @@ msgstr "Kodu Biçimlendir"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Başarıyla biçimlendirildi"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Genel Sertifika"
 msgstr "Genel Sertifika"
@@ -2092,7 +2220,7 @@ msgstr "Daha yüksek bir değer, daha iyi bağlantı yeniden kullanımı anlamı
 msgid "History"
 msgid "History"
 msgstr "Geçmiş"
 msgstr "Geçmiş"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Anasayfa"
 msgstr "Anasayfa"
 
 
@@ -2100,6 +2228,10 @@ msgstr "Anasayfa"
 msgid "Host"
 msgid "Host"
 msgstr "Host"
 msgstr "Host"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Saat"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "HTTP"
 msgstr "HTTP"
@@ -2284,6 +2416,10 @@ msgstr "Şifresi çözülmüş veride geçersiz dolgu"
 msgid "Invalid passcode or recovery code"
 msgid "Invalid passcode or recovery code"
 msgstr "Geçersiz parola veya kurtarma kodu"
 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
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Geçersiz kurtarma kodu"
 msgstr "Geçersiz kurtarma kodu"
@@ -2353,6 +2489,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Özel"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "En son şu tarihte kontrol edildi"
 msgstr "En son şu tarihte kontrol edildi"
@@ -2446,10 +2590,17 @@ msgstr "Veriler yükleniyor..."
 
 
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "Yerel"
 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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "Konum"
 msgstr "Konum"
@@ -2647,6 +2798,10 @@ msgstr "Minimum Boş Alan"
 msgid "Minimum free space in the cache directory"
 msgid "Minimum free space in the cache directory"
 msgstr "Önbellek dizinindeki minimum boş alan"
 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
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Dakika"
 msgstr "Dakika"
@@ -2680,11 +2835,24 @@ msgstr "Modül"
 msgid "Modules"
 msgid "Modules"
 msgstr "Modüller"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Çok Satırlı Yönergeler"
 msgstr "Çok Satırlı Yönergeler"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Nginx yapılandırması stream-enabled içermiyor"
 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "Nginx yapılandırma dizini ayarlanmamış"
 msgstr "Nginx yapılandırma dizini ayarlanmamış"
@@ -2921,6 +3094,11 @@ msgstr "Nginx teorik maksimum performansı"
 msgid "Nginx UI already installed"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI zaten yüklü"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "Nginx UI yapılandırması geri yüklendi"
 msgstr "Nginx UI yapılandırması geri yüklendi"
@@ -3100,6 +3278,7 @@ msgstr "Çevrimdışı"
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "Tamam"
 msgstr "Tamam"
 
 
@@ -3241,6 +3419,10 @@ msgstr "Şifreler eşleşmiyor"
 msgid "Path"
 msgid "Path"
 msgstr "Yol"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 msgstr "Yol: {0}, nginx yapılandırma dizini: {1} altında değil"
 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"
 msgid "Payload resource is nil"
 msgstr "Yük kaynağı 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Uygula"
 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"
 msgid "Please fill all fields correctly"
 msgstr "Lütfen tüm alanları doğru şekilde doldurun"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "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!"
 msgid "Please input your username!"
 msgstr "Lütfen kullanıcı adınızı girin!"
 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/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Lütfen giriş yapın."
 msgstr "Lütfen giriş yapın."
 
 
@@ -3394,7 +3585,7 @@ msgstr ""
 msgid "Please resolve all issues before proceeding with installation"
 msgid "Please resolve all issues before proceeding with installation"
 msgstr "Lütfen kuruluma devam etmeden önce tüm sorunları çözün"
 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:"
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
 msgstr ""
 "Lütfen bu güvenlik belirtecini kaydedin, geri yükleme için ihtiyacınız "
 "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"
 msgid "Running"
 msgstr "Koşma"
 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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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."
 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."
 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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3977,7 +4279,7 @@ msgstr "Güvenlik Ayarları"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Güvenlik Belirteci"
 msgstr "Güvenlik Belirteci"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Güvenlik Belirteci Bilgileri"
 msgstr "Güvenlik Belirteci Bilgileri"
 
 
@@ -4234,6 +4536,29 @@ msgstr "Durduruldu"
 msgid "Storage"
 msgid "Storage"
 msgstr "Depolama"
 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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "Yayın etkinleştirildi"
 msgstr "Yayın etkinleştirildi"
@@ -4258,10 +4583,17 @@ msgstr "Streams-enabled dizini mevcut değil"
 msgid "Stub Status Port"
 msgid "Stub Status Port"
 msgstr "Stub Durum Portu"
 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"
 msgid "Success"
 msgstr "Başarı"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4379,7 +4711,7 @@ msgstr "Senkronizasyon"
 msgid "System"
 msgid "System"
 msgstr "Sistem"
 msgstr "Sistem"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "Sistem Yedekleme"
 msgstr "Sistem Yedekleme"
 
 
@@ -4396,7 +4728,6 @@ msgid "System Restore"
 msgstr "Sistem Geri Yükleme"
 msgstr "Sistem Geri Yükleme"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "Sistem başarıyla geri yüklendi."
 msgstr "Sistem başarıyla geri yüklendi."
 
 
@@ -4420,6 +4751,10 @@ msgstr "Terminal"
 msgid "Terminal Start Command"
 msgid "Terminal Start Command"
 msgstr "Terminal Başlatma Komutu"
 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
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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 "
 "Bu işlem yalnızca sertifikayı veritabanından kaldıracaktır. Dosya "
 "sistemindeki sertifika dosyaları silinmeyecektir."
 "sistemindeki sertifika dosyaları silinmeyecektir."
 
 
-#: src/views/system/Backup/BackupCreator.vue:141
+#: src/views/backup/components/BackupCreator.vue:141
 msgid ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4639,6 +4974,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Kısıtlama"
 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/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4757,6 +5096,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Çöp"
 msgstr "Çöp"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Salı"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "İki faktörlü kimlik doğrulama gerekiyor"
 msgstr "İki faktörlü kimlik doğrulama gerekiyor"
@@ -4773,6 +5116,10 @@ msgstr "Tür"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Bilinmeyen"
 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
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Şifreyi Güncelle"
 msgstr "Şifreyi Güncelle"
@@ -4785,6 +5132,7 @@ msgstr "Profili Güncelle"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Başarıyla güncellendi"
 msgstr "Başarıyla güncellendi"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:234
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: 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"
 msgid "Updated at"
 msgstr "Güncellenme Tarihi"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4927,10 +5275,10 @@ msgstr "Görüntülendi"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Bekleme süreçleri"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Uyarı"
 msgstr "Uyarı"
 
 
@@ -4973,6 +5321,18 @@ msgstr "WebAuthn ayarları yapılandırılmamış"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "WebSocket bağlantı hatası"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
@@ -5020,7 +5380,7 @@ msgstr "Çalışan Süreçler"
 msgid "Workers"
 msgid "Workers"
 msgstr "Çalışanlar"
 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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Çalışma alanı"
 msgstr "Çalışma alanı"
@@ -5107,6 +5467,9 @@ msgstr "Eski kodlarınız artık çalışmayacak."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Geçiş Anahtarlarınız"
 msgstr "Geçiş Anahtarlarınız"
 
 
+#~ msgid "Last Backup Error"
+#~ msgstr "Son Yedekleme Hatası"
+
 #~ msgid "Apply"
 #~ msgid "Apply"
 #~ msgstr "Uygula"
 #~ msgstr "Uygula"
 
 
@@ -5131,9 +5494,6 @@ msgstr "Geçiş Anahtarlarınız"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "Tamam"
 #~ msgstr "Tamam"
 
 
-#~ msgid "Please fill in the required fields"
-#~ msgstr "Lütfen gerekli alanları doldurun"
-
 #~ msgid "Recover"
 #~ msgid "Recover"
 #~ msgstr "İyileşmek"
 #~ msgstr "İyileşmek"
 
 

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

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

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

@@ -103,7 +103,7 @@ msgstr "2fa"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Cài đặt 2FA"
 msgstr "Cài đặt 2FA"
 
 
-#: src/routes/modules/system.ts:45
+#: src/routes/modules/system.ts:38
 msgid "About"
 msgid "About"
 msgstr "Tác giả"
 msgstr "Tác giả"
 
 
@@ -129,6 +129,7 @@ msgstr "Người dùng ACME"
 msgid "Action"
 msgid "Action"
 msgstr "Hành động"
 msgstr "Hành động"
 
 
+#: src/views/backup/AutoBackup/AutoBackup.vue:241
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/ACMEUser.vue:90
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/CertificateList/certColumns.tsx:92
 #: src/views/certificate/DNSCredential.vue:30
 #: src/views/certificate/DNSCredential.vue:30
@@ -341,6 +342,11 @@ msgstr "Tự động"
 msgid "auto = CPU cores"
 msgid "auto = CPU cores"
 msgstr "auto = lõi cpu"
 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
 #: src/views/nginx_log/NginxLog.vue:150
 msgid "Auto Refresh"
 msgid "Auto Refresh"
 msgstr "Tự động làm mới"
 msgstr "Tự động làm mới"
@@ -382,7 +388,7 @@ msgstr "Trở về trang chủ"
 msgid "Back to List"
 msgid "Back to List"
 msgstr "Quay lại danh sách"
 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"
 msgid "Backup"
 msgstr "Sao lưu"
 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}"
 msgid "Backup file not found: {0}"
 msgstr "Không tìm thấy tệp sao lưu: {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"
 msgid "Backup has been downloaded successfully"
 msgstr "Đã tải xuống bản sao lưu thành công"
 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
 #: src/views/preference/tabs/AuthSettings.vue:97
 msgid "Ban Threshold Minutes"
 msgid "Ban Threshold Minutes"
 msgstr "Phút Ngưỡng Cấm"
 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"
 msgid "Block is nil"
 msgstr "Khối là 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
 #: src/views/system/About.vue:55
 msgid "Build with"
 msgid "Build with"
 msgstr "Xây dựng với"
 msgstr "Xây dựng với"
@@ -524,6 +566,14 @@ msgstr ""
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Huỷ"
 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
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
 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"
 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"
 msgid "Copied"
 msgstr "Đã sao chép"
 msgstr "Đã sao chép"
 
 
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copied!"
 msgid "Copied!"
 msgstr "Đã sao chép!"
 msgstr "Đã sao chép!"
 
 
 #: src/components/SensitiveString/SensitiveString.vue:37
 #: src/components/SensitiveString/SensitiveString.vue:37
-#: src/views/system/Backup/BackupCreator.vue:128
+#: src/views/backup/components/BackupCreator.vue:128
 msgid "Copy"
 msgid "Copy"
 msgstr "Sao chép"
 msgstr "Sao chép"
 
 
@@ -1003,7 +1053,7 @@ msgstr "Tạo"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Tạo thêm"
 msgstr "Tạo thêm"
 
 
-#: src/views/system/Backup/BackupCreator.vue:86
+#: src/views/backup/components/BackupCreator.vue:86
 msgid "Create Backup"
 msgid "Create Backup"
 msgstr "Tạo bản sao lưu"
 msgstr "Tạo bản sao lưu"
 
 
@@ -1015,7 +1065,7 @@ msgstr "Tạo tệp"
 msgid "Create Folder"
 msgid "Create Folder"
 msgstr "Tạo thư mục"
 msgstr "Tạo thư mục"
 
 
-#: src/views/system/Backup/BackupCreator.vue:75
+#: src/views/backup/components/BackupCreator.vue:75
 msgid ""
 msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 "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ạ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."
 "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/environments/group/columns.ts:29
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/notification/notificationColumns.tsx:51
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
 #: src/views/preference/components/AuthSettings/Passkey.vue:95
@@ -1047,6 +1098,10 @@ msgstr "Chứng chỉ"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Chứng chỉ"
 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
 #: src/views/preference/components/AuthSettings/TOTP.vue:72
 msgid "Current account is enabled TOTP."
 msgid "Current account is enabled TOTP."
 msgstr "Tài khoản hiện tại đã bật 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"
 msgid "Custom"
 msgstr "Tuỳ chỉnh"
 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
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "Customize the name of local node to be displayed in the environment "
 "indicator."
 "indicator."
 msgstr "Tùy chỉnh tên nút cục bộ để hiển thị trong chỉ báo môi trường."
 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/routes/modules/dashboard.ts:10 src/views/config/ConfigEditor.vue:110
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 #: src/views/config/ConfigEditor.vue:161 src/views/config/ConfigList.vue:67
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
 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
 #: src/views/preference/tabs/CertSettings.vue:32
 msgid "Days"
 msgid "Days"
 msgstr "Ngày"
 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"
 msgid "Disable stream %{name} from %{node} successfully"
 msgstr "Đã vô hiệu hóa luồng %{name} từ %{node} thành công"
 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:60
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/environments/list/envColumns.tsx:78
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: 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"
 msgid "Dynamic"
 msgstr "Động"
 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
 #: src/language/curd.ts:8
 msgid "Edit"
 msgid "Edit"
 msgstr "Chỉnh sửa"
 msgstr "Chỉnh sửa"
@@ -1550,6 +1635,8 @@ msgstr "Bật TLS"
 msgid "Enable TOTP"
 msgid "Enable TOTP"
 msgstr "Bật 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:69
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/environments/list/envColumns.tsx:75
 #: src/views/preference/tabs/HTTPSettings.vue:24
 #: src/views/preference/tabs/HTTPSettings.vue:24
@@ -1616,6 +1703,18 @@ msgstr "Lỗi xử lý nội dung"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Đường dẫn thực thi"
 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/components/CertInfo/CertInfo.vue:31
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 #: src/views/certificate/CertificateList/certColumns.tsx:80
 msgid "Expired"
 msgid "Expired"
@@ -1642,6 +1741,11 @@ msgstr "Thông báo bên ngoài"
 msgid "Fail to obtain certificate"
 msgid "Fail to obtain certificate"
 msgstr "Không thể lấy chứng chỉ"
 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
 #: src/constants/errors/docker.ts:4
 msgid "Failed to attach to exec instance: {0}"
 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}"
 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"
 msgid "Failed to create backup"
 msgstr "Không thể tạo bản sao lưu"
 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
 #: src/constants/errors/backup.ts:12
 msgid "Failed to create backup file: {0}"
 msgid "Failed to create backup file: {0}"
 msgstr "Không thể tạo tệp sao lưu: {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}"
 msgid "Failed to create restore directory: {0}"
 msgstr "Không thể tạo thư mục khôi phục: {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
 #: src/constants/errors/backup.ts:50
 msgid "Failed to create symbolic link: {0}"
 msgid "Failed to create symbolic link: {0}"
 msgstr "Không thể tạo liên kết tượng trưng: {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}"
 msgid "Failed to verify hashes: {0}"
 msgstr "Không thể xác minh băm: {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
 #: src/constants/errors/backup.ts:55
 msgid "Failed to write decrypted file: {0}"
 msgid "Failed to write decrypted file: {0}"
 msgstr "Không thể ghi tệp đã giải mã: {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}"
 msgid "Failed to write encrypted file: {0}"
 msgstr "Không thể ghi tệp đã mã hóa: {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
 #: src/constants/errors/backup.ts:33
 msgid "Failed to write to zip buffer: {0}"
 msgid "Failed to write to zip buffer: {0}"
 msgstr "Không thể ghi vào bộ đệm zip: {0}"
 msgstr "Không thể ghi vào bộ đệm zip: {0}"
@@ -1995,6 +2115,14 @@ msgstr "Định dạng code"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Định dạng thành công"
 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
 #: src/views/certificate/CertificateList/certColumns.tsx:30
 msgid "General Certificate"
 msgid "General Certificate"
 msgstr "Chứng chỉ chung"
 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"
 msgid "History"
 msgstr "Lịch sử"
 msgstr "Lịch sử"
 
 
-#: src/routes/index.ts:47
+#: src/routes/index.ts:48
 msgid "Home"
 msgid "Home"
 msgstr "Trang chủ"
 msgstr "Trang chủ"
 
 
@@ -2079,6 +2207,10 @@ msgstr "Trang chủ"
 msgid "Host"
 msgid "Host"
 msgstr "Máy chủ"
 msgstr "Máy chủ"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:159
+msgid "Hour"
+msgstr "Giờ"
+
 #: src/views/preference/Preference.vue:70
 #: src/views/preference/Preference.vue:70
 msgid "HTTP"
 msgid "HTTP"
 msgstr "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"
 msgid "Invalid passcode or recovery code"
 msgstr "Mã xác thực hoặc mã khôi phục không hợp lệ"
 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
 #: src/constants/errors/user.ts:5
 msgid "Invalid recovery code"
 msgid "Invalid recovery code"
 msgstr "Mã khôi phục không hợp lệ"
 msgstr "Mã khôi phục không hợp lệ"
@@ -2327,6 +2463,14 @@ msgstr "Lark"
 msgid "Lark Custom"
 msgid "Lark Custom"
 msgstr "Lark Tùy chỉnh"
 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
 #: src/views/system/Upgrade.vue:198
 msgid "Last checked at"
 msgid "Last checked at"
 msgstr "Kiểm tra lần cuối lúc"
 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/EnvIndicator/EnvIndicator.vue:39
 #: src/components/NodeSelector/NodeSelector.vue:86
 #: 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
 #: src/views/preference/tabs/NginxSettings.vue:55
 msgid "Local"
 msgid "Local"
 msgstr "Cục bộ"
 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
 #: src/components/NgxConfigEditor/LocationEditor.vue:69
 msgid "Location"
 msgid "Location"
 msgstr "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"
 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"
 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
 #: src/views/preference/tabs/LogrotateSettings.vue:30
 msgid "Minutes"
 msgid "Minutes"
 msgstr "Phút"
 msgstr "Phút"
@@ -2655,11 +2810,24 @@ msgstr "Mô-đun"
 msgid "Modules"
 msgid "Modules"
 msgstr "Mô-đun"
 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
 #: src/components/NgxConfigEditor/directive/DirectiveAdd.vue:51
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Chỉ thị nhiều dòng"
 msgstr "Chỉ thị nhiều dòng"
 
 
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
 #: src/components/NgxConfigEditor/NgxUpstream.vue:182
+#: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateEditor.vue:162
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx conf not include stream-enabled"
 msgstr "Cấu hình Nginx không bao gồm 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
 #: src/constants/errors/backup.ts:19
 msgid "Nginx config directory is not set"
 msgid "Nginx config directory is not set"
 msgstr "Thư mục cấu hình Nginx chưa được thiết lập"
 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"
 msgid "Nginx UI already installed"
 msgstr "Nginx UI đã được cài đặt"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:142
 msgid "Nginx UI configuration has been restored"
 msgid "Nginx UI configuration has been restored"
 msgstr "Cấu hình Nginx UI đã được khôi phục"
 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/NgxServer.vue:53
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/NgxConfigEditor/NgxUpstream.vue:36
 #: src/components/Notification/Notification.vue:110 src/language/curd.ts:15
 #: 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/notification/Notification.vue:39
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/components/SiteStatusSelect.vue:123
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:39
 #: 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/site/site_list/SiteList.vue:99
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/components/RightPanel/Basic.vue:46
 #: src/views/stream/StreamList.vue:156
 #: src/views/stream/StreamList.vue:156
-#: src/views/system/Backup/BackupCreator.vue:149
 msgid "OK"
 msgid "OK"
 msgstr "Đồng ý"
 msgstr "Đồng ý"
 
 
@@ -3215,6 +3393,10 @@ msgstr "Mật khẩu không khớp"
 msgid "Path"
 msgid "Path"
 msgstr "Đường dẫn"
 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
 #: src/constants/errors/cert.ts:7 src/constants/errors/config.ts:2
 msgid "Path: {0} is not under the nginx conf dir: {1}"
 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}"
 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"
 msgid "Payload resource is nil"
 msgstr "Tài nguyên tải trọng là 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
 #: src/views/environments/list/BatchUpgrader.vue:232
 msgid "Perform"
 msgid "Perform"
 msgstr "Thực hiện"
 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"
 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"
 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
 #: src/views/certificate/DNSCredential.vue:52
 msgid ""
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "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!"
 msgid "Please input your username!"
 msgstr "Vui lòng nhập username!"
 msgstr "Vui lòng nhập username!"
 
 
+#: src/views/backup/components/SystemRestore.vue:8
 #: src/views/install/components/InstallView.vue:48
 #: src/views/install/components/InstallView.vue:48
-#: src/views/system/Backup/SystemRestore.vue:10
 msgid "Please log in."
 msgid "Please log in."
 msgstr "Vui lòng đăng nhập."
 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"
 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"
 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:"
 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:"
 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"
 msgid "Running"
 msgstr "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/ChatGPT/ChatGPT.vue:355
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
 #: 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."
 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."
 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
 #: src/views/certificate/components/DNSChallenge.vue:90
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
@@ -3933,7 +4235,7 @@ msgstr "Cài đặt bảo mật"
 msgid "Security Token"
 msgid "Security Token"
 msgstr "Mã bảo mật"
 msgstr "Mã bảo mật"
 
 
-#: src/views/system/Backup/BackupCreator.vue:94
+#: src/views/backup/components/BackupCreator.vue:94
 msgid "Security Token Information"
 msgid "Security Token Information"
 msgstr "Thông tin mã bảo mật"
 msgstr "Thông tin mã bảo mật"
 
 
@@ -4190,6 +4492,29 @@ msgstr "Đã dừng"
 msgid "Storage"
 msgid "Storage"
 msgstr "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
 #: src/constants/errors/stream.ts:4
 msgid "Stream is enabled"
 msgid "Stream is enabled"
 msgstr "Luồng đã được bật"
 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"
 msgid "Stub Status Port"
 msgstr "Cổng trạng thái stub"
 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"
 msgid "Success"
 msgstr "Thành công"
 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
 #: src/components/SelfCheck/tasks/frontend/sse.ts:14
 msgid ""
 msgid ""
 "Support communication with the backend through the Server-Sent Events "
 "Support communication with the backend through the Server-Sent Events "
@@ -4333,7 +4665,7 @@ msgstr "Đồng bộ hóa"
 msgid "System"
 msgid "System"
 msgstr "Thông tin"
 msgstr "Thông tin"
 
 
-#: src/views/system/Backup/BackupCreator.vue:71
+#: src/views/backup/components/BackupCreator.vue:71
 msgid "System Backup"
 msgid "System Backup"
 msgstr "Sao lưu hệ thống"
 msgstr "Sao lưu hệ thống"
 
 
@@ -4350,7 +4682,6 @@ msgid "System Restore"
 msgstr "Khôi phục hệ thống"
 msgstr "Khôi phục hệ thống"
 
 
 #: src/views/install/components/InstallView.vue:44
 #: src/views/install/components/InstallView.vue:44
-#: src/views/system/Backup/SystemRestore.vue:6
 msgid "System restored successfully."
 msgid "System restored successfully."
 msgstr "Hệ thống đã được khôi phục thành công."
 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"
 msgid "Terminal Start Command"
 msgstr "Lệnh Khởi động Terminal"
 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
 #: src/components/AutoCertForm/AutoCertForm.vue:48
 msgid ""
 msgid ""
 "The certificate for the domain will be checked 30 minutes, and will be "
 "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ỉ "
 "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."
 "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 ""
 msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 "make sure to save it in a secure location."
@@ -4593,6 +4928,10 @@ msgstr ""
 msgid "Throttle"
 msgid "Throttle"
 msgstr "Hạn chế"
 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/components/AuthSettings/AddPasskey.vue:65
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/AuthSettings.vue:112
 #: src/views/preference/tabs/LogrotateSettings.vue:12
 #: src/views/preference/tabs/LogrotateSettings.vue:12
@@ -4711,6 +5050,10 @@ msgstr ""
 msgid "Trash"
 msgid "Trash"
 msgstr "Thùng rác"
 msgstr "Thùng rác"
 
 
+#: src/views/backup/AutoBackup/components/CronEditor.vue:28
+msgid "Tuesday"
+msgstr "Thứ Ba"
+
 #: src/components/TwoFA/use2FAModal.ts:67
 #: src/components/TwoFA/use2FAModal.ts:67
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Yêu cầu xác thực hai yếu tố"
 msgstr "Yêu cầu xác thực hai yếu tố"
@@ -4727,6 +5070,10 @@ msgstr "Loại"
 msgid "Unknown"
 msgid "Unknown"
 msgstr "Không xác định"
 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
 #: src/views/user/UserProfile.vue:218
 msgid "Update Password"
 msgid "Update Password"
 msgstr "Cập nhật mật khẩu"
 msgstr "Cập nhật mật khẩu"
@@ -4739,6 +5086,7 @@ msgstr "Cập nhật hồ sơ"
 msgid "Update successfully"
 msgid "Update successfully"
 msgstr "Cập nhật thành công"
 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/ACMEUser.vue:83
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/certificate/DNSCredential.vue:24
 #: src/views/config/configColumns.tsx:35 src/views/config/ConfigEditor.vue:335
 #: 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"
 msgid "Updated at"
 msgstr "Ngày cập nhật"
 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:100
 #: src/views/environments/list/Environment.vue:108
 #: src/views/environments/list/Environment.vue:108
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
 #: src/views/system/Upgrade.vue:154 src/views/system/Upgrade.vue:159
@@ -4881,10 +5229,10 @@ msgstr "Đã xem"
 msgid "Waiting processes"
 msgid "Waiting processes"
 msgstr "Quá trình chờ đợi"
 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/notification/notificationColumns.tsx:21
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:82
-#: src/views/system/Backup/BackupCreator.vue:138
 msgid "Warning"
 msgid "Warning"
 msgstr "Lưu ý"
 msgstr "Lưu ý"
 
 
@@ -4927,6 +5275,18 @@ msgstr "Cài đặt WebAuthn chưa được cấu hình"
 msgid "WebSocket connection error"
 msgid "WebSocket connection error"
 msgstr "Lỗi kết nối WebSocket"
 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
 #: src/views/certificate/ACMEUser.vue:78
 msgid ""
 msgid ""
 "When Enabled, Nginx UI will automatically re-register users upon startup. "
 "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"
 msgid "Workers"
 msgstr "Công nhân"
 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
 #: src/views/workspace/WorkSpace.vue:52
 msgid "Workspace"
 msgid "Workspace"
 msgstr "Không gian làm việc"
 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"
 msgid "Your passkeys"
 msgstr "Khóa truy cập của bạn"
 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"
 #~ msgid "Apply"
 #~ msgstr "Áp dụng"
 #~ msgstr "Áp dụng"
 
 
@@ -5080,9 +5443,6 @@ msgstr "Khóa truy cập của bạn"
 #~ msgid "Ok"
 #~ msgid "Ok"
 #~ msgstr "Đồng ý"
 #~ 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"
 #~ msgid "Recover"
 #~ msgstr "Khôi phục"
 #~ msgstr "Khôi phục"
 
 

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

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

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

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

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

@@ -45,18 +45,19 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           collapsed: false,
           items: [
           items: [
             { text: 'App', link: '/guide/config-app' },
             { 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: 'Auth', link: '/guide/config-auth' },
+            { text: 'Backup', link: '/guide/config-backup' },
             { text: 'Casdoor', link: '/guide/config-casdoor' },
             { text: 'Casdoor', link: '/guide/config-casdoor' },
             { text: 'Cert', link: '/guide/config-cert' },
             { text: 'Cert', link: '/guide/config-cert' },
             { text: 'Cluster', link: '/guide/config-cluster' },
             { text: 'Cluster', link: '/guide/config-cluster' },
             { text: 'Crypto', link: '/guide/config-crypto' },
             { text: 'Crypto', link: '/guide/config-crypto' },
+            { text: 'Database', link: '/guide/config-database' },
             { text: 'Http', link: '/guide/config-http' },
             { text: 'Http', link: '/guide/config-http' },
             { text: 'Logrotate', link: '/guide/config-logrotate' },
             { text: 'Logrotate', link: '/guide/config-logrotate' },
             { text: 'Nginx', link: '/guide/config-nginx' },
             { text: 'Nginx', link: '/guide/config-nginx' },
             { text: 'Node', link: '/guide/config-node' },
             { text: 'Node', link: '/guide/config-node' },
             { text: 'Open AI', link: '/guide/config-openai' },
             { text: 'Open AI', link: '/guide/config-openai' },
+            { text: 'Server', link: '/guide/config-server' },
             { text: 'Terminal', link: '/guide/config-terminal' },
             { text: 'Terminal', link: '/guide/config-terminal' },
             { text: 'Webauthn', link: '/guide/config-webauthn' }
             { 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,
           collapsed: false,
           items: [
           items: [
             { text: 'App', link: '/zh_CN/guide/config-app' },
             { 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: 'Auth', link: '/zh_CN/guide/config-auth' },
+            { text: 'Backup', link: '/zh_CN/guide/config-backup' },
             { text: 'Casdoor', link: '/zh_CN/guide/config-casdoor' },
             { text: 'Casdoor', link: '/zh_CN/guide/config-casdoor' },
             { text: 'Cert', link: '/zh_CN/guide/config-cert' },
             { text: 'Cert', link: '/zh_CN/guide/config-cert' },
             { text: 'Cluster', link: '/zh_CN/guide/config-cluster' },
             { text: 'Cluster', link: '/zh_CN/guide/config-cluster' },
             { text: 'Crypto', link: '/zh_CN/guide/config-crypto' },
             { 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: 'Http', link: '/zh_CN/guide/config-http' },
             { text: 'Logrotate', link: '/zh_CN/guide/config-logrotate' },
             { text: 'Logrotate', link: '/zh_CN/guide/config-logrotate' },
             { text: 'Nginx', link: '/zh_CN/guide/config-nginx' },
             { text: 'Nginx', link: '/zh_CN/guide/config-nginx' },
             { text: 'Node', link: '/zh_CN/guide/config-node' },
             { text: 'Node', link: '/zh_CN/guide/config-node' },
             { text: 'Open AI', link: '/zh_CN/guide/config-openai' },
             { 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: 'Terminal', link: '/zh_CN/guide/config-terminal' },
             { text: 'Webauthn', link: '/zh_CN/guide/config-webauthn' }
             { 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,
           collapsed: false,
           items: [
           items: [
             { text: 'App', link: '/zh_TW/guide/config-app' },
             { 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: 'Auth', link: '/zh_TW/guide/config-auth' },
+            { text: 'Backup', link: '/zh_TW/guide/config-backup' },
             { text: 'Casdoor', link: '/zh_TW/guide/config-casdoor' },
             { text: 'Casdoor', link: '/zh_TW/guide/config-casdoor' },
             { text: 'Cert', link: '/zh_TW/guide/config-cert' },
             { text: 'Cert', link: '/zh_TW/guide/config-cert' },
             { text: 'Cluster', link: '/zh_TW/guide/config-cluster' },
             { text: 'Cluster', link: '/zh_TW/guide/config-cluster' },
             { text: 'Crypto', link: '/zh_TW/guide/config-crypto' },
             { 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: 'Http', link: '/zh_TW/guide/config-http' },
             { text: 'Logrotate', link: '/zh_TW/guide/config-logrotate' },
             { text: 'Logrotate', link: '/zh_TW/guide/config-logrotate' },
             { text: 'Nginx', link: '/zh_TW/guide/config-nginx' },
             { text: 'Nginx', link: '/zh_TW/guide/config-nginx' },
             { text: 'Node', link: '/zh_TW/guide/config-node' },
             { text: 'Node', link: '/zh_TW/guide/config-node' },
             { text: 'Open AI', link: '/zh_TW/guide/config-openai' },
             { 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: 'Terminal', link: '/zh_TW/guide/config-terminal' },
             { text: 'Webauthn', link: '/zh_TW/guide/config-webauthn' }
             { 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/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
 	github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // 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 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/config v1.29.14 // indirect
 	github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // 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/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/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/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/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/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/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/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/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/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/ssooidc v1.30.1 // indirect
 	github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // 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.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 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
 github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
 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 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/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
 github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
 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/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 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/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/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 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/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 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/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 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/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 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/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 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/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=
 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"
 	"github.com/uozi-tech/cosy/logger"
 )
 )
 
 
-// Directory and file names
+// Constants for backup directory and file naming conventions
 const (
 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 {
 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 {
 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) {
 func Backup() (BackupResult, error) {
-	// Generate timestamps for filenames
+	// Generate timestamp for unique backup identification
 	timestamp := time.Now().Format("20060102-150405")
 	timestamp := time.Now().Format("20060102-150405")
 	backupName := fmt.Sprintf("backup-%s.zip", timestamp)
 	backupName := fmt.Sprintf("backup-%s.zip", timestamp)
 
 
-	// Generate AES key and IV
+	// Generate cryptographic keys for AES encryption
 	key, err := GenerateAESKey()
 	key, err := GenerateAESKey()
 	if err != nil {
 	if err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrGenerateAESKey, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrGenerateAESKey, err.Error())
@@ -57,14 +72,14 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrGenerateIV, err.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-*")
 	tempDir, err := os.MkdirTemp("", "nginx-ui-backup-*")
 	if err != nil {
 	if err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateTempDir, err.Error())
 		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)
 	nginxUITempDir := filepath.Join(tempDir, NginxUIDir)
 	nginxTempDir := filepath.Join(tempDir, NginxDir)
 	nginxTempDir := filepath.Join(tempDir, NginxDir)
 	if err := os.MkdirAll(nginxUITempDir, 0755); err != nil {
 	if err := os.MkdirAll(nginxUITempDir, 0755); err != nil {
@@ -74,30 +89,31 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateTempSubDir, err.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 {
 	if err := backupNginxUIFiles(nginxUITempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrBackupNginxUI, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrBackupNginxUI, err.Error())
 	}
 	}
 
 
-	// Backup nginx configs to a directory
+	// Stage Nginx configuration files
 	if err := backupNginxFiles(nginxTempDir); err != nil {
 	if err := backupNginxFiles(nginxTempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrBackupNginx, err.Error())
 		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)
 	nginxUIZipPath := filepath.Join(tempDir, NginxUIZipName)
 	nginxZipPath := filepath.Join(tempDir, NginxZipName)
 	nginxZipPath := filepath.Join(tempDir, NginxZipName)
 
 
-	// Create zip archives for each directory
+	// Compress Nginx UI files into archive
 	if err := createZipArchive(nginxUIZipPath, nginxUITempDir); err != nil {
 	if err := createZipArchive(nginxUIZipPath, nginxUITempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 	}
 	}
 
 
+	// Compress Nginx configuration files into archive
 	if err := createZipArchive(nginxZipPath, nginxTempDir); err != nil {
 	if err := createZipArchive(nginxZipPath, nginxTempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 	}
 	}
 
 
-	// Calculate hashes for the zip files
+	// Calculate cryptographic hashes for integrity verification
 	nginxUIHash, err := calculateFileHash(nginxUIZipPath)
 	nginxUIHash, err := calculateFileHash(nginxUIZipPath)
 	if err != nil {
 	if err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
@@ -108,10 +124,10 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCalculateHash, err.Error())
 	}
 	}
 
 
-	// Get current version information
+	// Gather version information for backup metadata
 	versionInfo := version.GetVersionInfo()
 	versionInfo := version.GetVersionInfo()
 
 
-	// Create hash info file
+	// Create hash verification file with metadata
 	hashInfo := HashInfo{
 	hashInfo := HashInfo{
 		NginxUIHash: nginxUIHash,
 		NginxUIHash: nginxUIHash,
 		NginxHash:   nginxHash,
 		NginxHash:   nginxHash,
@@ -119,13 +135,13 @@ func Backup() (BackupResult, error) {
 		Version:     versionInfo.Version,
 		Version:     versionInfo.Version,
 	}
 	}
 
 
-	// Write hash info to file
+	// Write hash information to verification file
 	hashInfoPath := filepath.Join(tempDir, HashInfoFile)
 	hashInfoPath := filepath.Join(tempDir, HashInfoFile)
 	if err := writeHashInfoFile(hashInfoPath, hashInfo); err != nil {
 	if err := writeHashInfoFile(hashInfoPath, hashInfo); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateHashFile, err.Error())
 		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 {
 	if err := encryptFile(hashInfoPath, key, iv); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrEncryptFile, HashInfoFile)
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrEncryptFile, HashInfoFile)
 	}
 	}
@@ -138,7 +154,7 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrEncryptNginxDir, err.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 {
 	if err := os.RemoveAll(nginxUITempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCleanupTempDir, err.Error())
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCleanupTempDir, err.Error())
 	}
 	}
@@ -146,17 +162,17 @@ func Backup() (BackupResult, error) {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCleanupTempDir, err.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
 	var buffer bytes.Buffer
 	if err := createZipArchiveToBuffer(&buffer, tempDir); err != nil {
 	if err := createZipArchiveToBuffer(&buffer, tempDir); err != nil {
 		return BackupResult{}, cosy.WrapErrorWithParams(ErrCreateZipArchive, err.Error())
 		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)
 	keyBase64 := base64.StdEncoding.EncodeToString(key)
 	ivBase64 := base64.StdEncoding.EncodeToString(iv)
 	ivBase64 := base64.StdEncoding.EncodeToString(iv)
 
 
-	// Return result
+	// Assemble final backup result
 	result := BackupResult{
 	result := BackupResult{
 		BackupContent: buffer.Bytes(),
 		BackupContent: buffer.Bytes(),
 		BackupName:    backupName,
 		BackupName:    backupName,
@@ -164,6 +180,6 @@ func Backup() (BackupResult, error) {
 		AESIv:         ivBase64,
 		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
 	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}")
 	ErrCalculateUIHash    = errScope.New(4802, "Failed to calculate Nginx UI hash: {0}")
 	ErrCalculateNginxHash = errScope.New(4803, "Failed to calculate Nginx hash: {0}")
 	ErrCalculateNginxHash = errScope.New(4803, "Failed to calculate Nginx hash: {0}")
 	ErrHashMismatch       = errScope.New(4804, "Hash verification failed: file integrity compromised")
 	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"
 	"io"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
+	"strings"
 
 
+	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/uozi-tech/cosy"
 	"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 {
 func copyFile(src, dst string) error {
-	// Open source file
+	// Open source file for reading
 	source, err := os.Open(src)
 	source, err := os.Open(src)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	defer source.Close()
 	defer source.Close()
 
 
-	// Create destination file
+	// Create destination file for writing
 	destination, err := os.Create(dst)
 	destination, err := os.Create(dst)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 	defer destination.Close()
 	defer destination.Close()
 
 
-	// Copy content
+	// Copy file content from source to destination
 	_, err = io.Copy(destination, source)
 	_, err = io.Copy(destination, source)
 	return err
 	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 {
 func copyDirectory(src, dst string) error {
-	// Check if source is a directory
+	// Verify source is a directory
 	srcInfo, err := os.Stat(src)
 	srcInfo, err := os.Stat(src)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -40,18 +153,18 @@ func copyDirectory(src, dst string) error {
 		return cosy.WrapErrorWithParams(ErrCopyNginxConfigDir, "%s is not a directory", src)
 		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 {
 	if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil {
 		return err
 		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 {
 	return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 
 
-		// Calculate relative path
+		// Calculate relative path from source root
 		relPath, err := filepath.Rel(src, path)
 		relPath, err := filepath.Rel(src, path)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -60,26 +173,24 @@ func copyDirectory(src, dst string) error {
 			return nil
 			return nil
 		}
 		}
 
 
-		// Create target path
+		// Construct target path
 		targetPath := filepath.Join(dst, relPath)
 		targetPath := filepath.Join(dst, relPath)
 
 
-		// Check if it's a symlink
+		// Handle symbolic links by recreating them
 		if info.Mode()&os.ModeSymlink != 0 {
 		if info.Mode()&os.ModeSymlink != 0 {
-			// Read the link
 			linkTarget, err := os.Readlink(path)
 			linkTarget, err := os.Readlink(path)
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
-			// Create symlink at target path
 			return os.Symlink(linkTarget, targetPath)
 			return os.Symlink(linkTarget, targetPath)
 		}
 		}
 
 
-		// If it's a directory, create it
+		// Create directories with original permissions
 		if info.IsDir() {
 		if info.IsDir() {
 			return os.MkdirAll(targetPath, info.Mode())
 			return os.MkdirAll(targetPath, info.Mode())
 		}
 		}
 
 
-		// If it's a file, copy it
+		// Copy regular files
 		return copyFile(path, targetPath)
 		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)
 		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
 	// Start the scheduler
 	s.Start()
 	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{},
 		Passkey{},
 		EnvGroup{},
 		EnvGroup{},
 		ExternalNotify{},
 		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)
 	Q              = new(Query)
 	AcmeUser       *acmeUser
 	AcmeUser       *acmeUser
 	AuthToken      *authToken
 	AuthToken      *authToken
+	AutoBackup     *autoBackup
 	BanIP          *banIP
 	BanIP          *banIP
 	Cert           *cert
 	Cert           *cert
 	ChatGPTLog     *chatGPTLog
 	ChatGPTLog     *chatGPTLog
@@ -39,6 +40,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 	*Q = *Use(db, opts...)
 	*Q = *Use(db, opts...)
 	AcmeUser = &Q.AcmeUser
 	AcmeUser = &Q.AcmeUser
 	AuthToken = &Q.AuthToken
 	AuthToken = &Q.AuthToken
+	AutoBackup = &Q.AutoBackup
 	BanIP = &Q.BanIP
 	BanIP = &Q.BanIP
 	Cert = &Q.Cert
 	Cert = &Q.Cert
 	ChatGPTLog = &Q.ChatGPTLog
 	ChatGPTLog = &Q.ChatGPTLog
@@ -60,6 +62,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 		db:             db,
 		db:             db,
 		AcmeUser:       newAcmeUser(db, opts...),
 		AcmeUser:       newAcmeUser(db, opts...),
 		AuthToken:      newAuthToken(db, opts...),
 		AuthToken:      newAuthToken(db, opts...),
+		AutoBackup:     newAutoBackup(db, opts...),
 		BanIP:          newBanIP(db, opts...),
 		BanIP:          newBanIP(db, opts...),
 		Cert:           newCert(db, opts...),
 		Cert:           newCert(db, opts...),
 		ChatGPTLog:     newChatGPTLog(db, opts...),
 		ChatGPTLog:     newChatGPTLog(db, opts...),
@@ -82,6 +85,7 @@ type Query struct {
 
 
 	AcmeUser       acmeUser
 	AcmeUser       acmeUser
 	AuthToken      authToken
 	AuthToken      authToken
+	AutoBackup     autoBackup
 	BanIP          banIP
 	BanIP          banIP
 	Cert           cert
 	Cert           cert
 	ChatGPTLog     chatGPTLog
 	ChatGPTLog     chatGPTLog
@@ -105,6 +109,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
 		db:             db,
 		db:             db,
 		AcmeUser:       q.AcmeUser.clone(db),
 		AcmeUser:       q.AcmeUser.clone(db),
 		AuthToken:      q.AuthToken.clone(db),
 		AuthToken:      q.AuthToken.clone(db),
+		AutoBackup:     q.AutoBackup.clone(db),
 		BanIP:          q.BanIP.clone(db),
 		BanIP:          q.BanIP.clone(db),
 		Cert:           q.Cert.clone(db),
 		Cert:           q.Cert.clone(db),
 		ChatGPTLog:     q.ChatGPTLog.clone(db),
 		ChatGPTLog:     q.ChatGPTLog.clone(db),
@@ -135,6 +140,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 		db:             db,
 		db:             db,
 		AcmeUser:       q.AcmeUser.replaceDB(db),
 		AcmeUser:       q.AcmeUser.replaceDB(db),
 		AuthToken:      q.AuthToken.replaceDB(db),
 		AuthToken:      q.AuthToken.replaceDB(db),
+		AutoBackup:     q.AutoBackup.replaceDB(db),
 		BanIP:          q.BanIP.replaceDB(db),
 		BanIP:          q.BanIP.replaceDB(db),
 		Cert:           q.Cert.replaceDB(db),
 		Cert:           q.Cert.replaceDB(db),
 		ChatGPTLog:     q.ChatGPTLog.replaceDB(db),
 		ChatGPTLog:     q.ChatGPTLog.replaceDB(db),
@@ -155,6 +161,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 type queryCtx struct {
 type queryCtx struct {
 	AcmeUser       *acmeUserDo
 	AcmeUser       *acmeUserDo
 	AuthToken      *authTokenDo
 	AuthToken      *authTokenDo
+	AutoBackup     *autoBackupDo
 	BanIP          *banIPDo
 	BanIP          *banIPDo
 	Cert           *certDo
 	Cert           *certDo
 	ChatGPTLog     *chatGPTLogDo
 	ChatGPTLog     *chatGPTLogDo
@@ -175,6 +182,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
 	return &queryCtx{
 	return &queryCtx{
 		AcmeUser:       q.AcmeUser.WithContext(ctx),
 		AcmeUser:       q.AcmeUser.WithContext(ctx),
 		AuthToken:      q.AuthToken.WithContext(ctx),
 		AuthToken:      q.AuthToken.WithContext(ctx),
+		AutoBackup:     q.AutoBackup.WithContext(ctx),
 		BanIP:          q.BanIP.WithContext(ctx),
 		BanIP:          q.BanIP.WithContext(ctx),
 		Cert:           q.Cert.WithContext(ctx),
 		Cert:           q.Cert.WithContext(ctx),
 		ChatGPTLog:     q.ChatGPTLog.WithContext(ctx),
 		ChatGPTLog:     q.ChatGPTLog.WithContext(ctx),

+ 4 - 1
router/routers.go

@@ -6,6 +6,7 @@ import (
 	"github.com/gin-contrib/pprof"
 	"github.com/gin-contrib/pprof"
 
 
 	"github.com/0xJacky/Nginx-UI/api/analytic"
 	"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/certificate"
 	"github.com/0xJacky/Nginx-UI/api/cluster"
 	"github.com/0xJacky/Nginx-UI/api/cluster"
 	"github.com/0xJacky/Nginx-UI/api/config"
 	"github.com/0xJacky/Nginx-UI/api/config"
@@ -52,10 +53,11 @@ func InitRouter() {
 		public.InitRouter(root)
 		public.InitRouter(root)
 		crypto.InitPublicRouter(root)
 		crypto.InitPublicRouter(root)
 		user.InitAuthRouter(root)
 		user.InitAuthRouter(root)
-		
+
 		system.InitPublicRouter(root)
 		system.InitPublicRouter(root)
 		system.InitBackupRestoreRouter(root)
 		system.InitBackupRestoreRouter(root)
 		system.InitSelfCheckRouter(root)
 		system.InitSelfCheckRouter(root)
+		backup.InitRouter(root)
 
 
 		// Authorization required and not websocket request
 		// Authorization required and not websocket request
 		g := root.Group("/", middleware.AuthRequired(), middleware.Proxy())
 		g := root.Group("/", middleware.AuthRequired(), middleware.Proxy())
@@ -80,6 +82,7 @@ func InitRouter() {
 			cluster.InitRouter(g)
 			cluster.InitRouter(g)
 			notification.InitRouter(g)
 			notification.InitRouter(g)
 			external_notify.InitRouter(g)
 			external_notify.InitRouter(g)
+			backup.InitAutoBackupRouter(g)
 		}
 		}
 
 
 		// Authorization required and websocket request
 		// 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,
 	"OPENAI":    OpenAISettings,
 	"TERMINAL":  TerminalSettings,
 	"TERMINAL":  TerminalSettings,
 	"WEBAUTHN":  WebAuthnSettings,
 	"WEBAUTHN":  WebAuthnSettings,
+	"BACKUP":    BackupSettings,
 }
 }
 
 
 func init() {
 func init() {
@@ -46,6 +47,7 @@ func init() {
 
 
 	sections.Set("database", DatabaseSettings)
 	sections.Set("database", DatabaseSettings)
 	sections.Set("auth", AuthSettings)
 	sections.Set("auth", AuthSettings)
+	sections.Set("backup", BackupSettings)
 	sections.Set("casdoor", CasdoorSettings)
 	sections.Set("casdoor", CasdoorSettings)
 	sections.Set("cert", CertSettings)
 	sections.Set("cert", CertSettings)
 	sections.Set("cluster", ClusterSettings)
 	sections.Set("cluster", ClusterSettings)