You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: [#315] Step 2.4 - Update create template command to include backup with defaults
- Include BackupSection in template with default values
- Default schedule: 3:00 AM daily (0 3 * * *)
- Default retention: 7 days
- Enables backup support for users immediately upon creation
- Regenerate JSON schema to include BackupSection definition
- Schema now documents schedule and retention_days fields
- Provides IDE autocomplete and validation for backup configuration
- Follows schema regeneration instructions from schemas/README.md
- Update comment to clarify that backups are now enabled by default
- Users can remove the backup section if not needed
- Follows the same pattern as prometheus and grafana
Relates to: #315
"description": "Backup configuration (optional)\n\nWhen present, automated backups will be configured for the tracker\ndatabase and other persistent data.\n\nUses `BackupSection` for JSON parsing with String primitives (cron schedule).\nConverted to domain `BackupConfig` via `TryInto<EnvironmentParams>`.\n\nDefault schedule: 3:00 AM daily (\"0 3 * * *\")\nDefault retention: 7 days",
9
+
"anyOf": [
10
+
{
11
+
"$ref": "#/$defs/BackupSection"
12
+
},
13
+
{
14
+
"type": "null"
15
+
}
16
+
],
17
+
"default": null
18
+
},
9
19
"environment": {
10
20
"description": "Environment-specific settings",
11
21
"$ref": "#/$defs/EnvironmentSection"
12
22
},
13
23
"grafana": {
14
-
"description": "Grafana dashboard configuration (optional)\n\nWhen present, Grafana will be deployed for visualization.\n**Requires Prometheus to be configured** - Grafana depends on\nPrometheus as its data source.\n\nUses `GrafanaSection` for JSON parsing with String primitives.\nConverted to domain `GrafanaConfig` via `to_environment_params()`.",
24
+
"description": "Grafana dashboard configuration (optional)\n\nWhen present, Grafana will be deployed for visualization.\n**Requires Prometheus to be configured** - Grafana depends on\nPrometheus as its data source.\n\nUses `GrafanaSection` for JSON parsing with String primitives.\nConverted to domain `GrafanaConfig` via `TryInto<EnvironmentParams>`.",
15
25
"anyOf": [
16
26
{
17
27
"$ref": "#/$defs/GrafanaSection"
@@ -35,7 +45,7 @@
35
45
"default": null
36
46
},
37
47
"prometheus": {
38
-
"description": "Prometheus monitoring configuration (optional)\n\nWhen present, Prometheus will be deployed to monitor the tracker.\nUses `PrometheusSection` for JSON parsing with String primitives.\nConverted to domain `PrometheusConfig` via `to_environment_params()`.",
48
+
"description": "Prometheus monitoring configuration (optional)\n\nWhen present, Prometheus will be deployed to monitor the tracker.\nUses `PrometheusSection` for JSON parsing with String primitives.\nConverted to domain `PrometheusConfig` via `TryInto<EnvironmentParams>`.",
39
49
"anyOf": [
40
50
{
41
51
"$ref": "#/$defs/PrometheusSection"
@@ -47,15 +57,15 @@
47
57
"default": null
48
58
},
49
59
"provider": {
50
-
"description": "Provider-specific configuration (LXD, Hetzner, etc.)\n\nUses `ProviderSection` for JSON parsing with raw primitives.\nConverted to domain `ProviderConfig` via `to_environment_params()`.",
60
+
"description": "Provider-specific configuration (LXD, Hetzner, etc.)\n\nUses `ProviderSection` for JSON parsing with raw primitives.\nConverted to domain `ProviderConfig` via `TryInto<EnvironmentParams>`.",
51
61
"$ref": "#/$defs/ProviderSection"
52
62
},
53
63
"ssh_credentials": {
54
64
"description": "SSH credentials configuration",
55
65
"$ref": "#/$defs/SshCredentialsConfig"
56
66
},
57
67
"tracker": {
58
-
"description": "Tracker deployment configuration\n\nUses `TrackerSection` for JSON parsing with String primitives.\nConverted to domain `TrackerConfig` via `to_environment_params()`.",
68
+
"description": "Tracker deployment configuration\n\nUses `TrackerSection` for JSON parsing with String primitives.\nConverted to domain `TrackerConfig` via `TryInto<EnvironmentParams>`.",
59
69
"$ref": "#/$defs/TrackerSection"
60
70
}
61
71
},
@@ -66,6 +76,24 @@
66
76
"tracker"
67
77
],
68
78
"$defs": {
79
+
"BackupSection": {
80
+
"description": "Backup configuration section (DTO)\n\nOptional configuration for automated backups. If present, backup support\nis enabled with the specified schedule and retention policy.\n\n# Examples\n\n```json\n{\n\"schedule\": \"0 3 * * *\",\n\"retention_days\": 7\n}\n```\n\nAll fields have defaults, so you can enable backup with minimal config:\n\n```json\n{\n\"backup\": {}\n}\n```",
81
+
"type": "object",
82
+
"properties": {
83
+
"retention_days": {
84
+
"description": "Number of days to retain backups before automatic deletion\n\nDefault: 7 days\n\nMust be greater than 0.",
85
+
"type": "integer",
86
+
"format": "uint32",
87
+
"default": 7,
88
+
"minimum": 0
89
+
},
90
+
"schedule": {
91
+
"description": "Cron schedule for backups (5-field format: minute hour day month weekday)\n\nDefault: \"0 3 * * *\" (3:00 AM daily)\n\nExamples:\n- \"0 3 * * *\" - 3:00 AM daily\n- \"0 */6 * * *\" - Every 6 hours\n- \"0 0 * * 0\" - Midnight every Sunday",
92
+
"type": "string",
93
+
"default": "0 3 * * *"
94
+
}
95
+
}
96
+
},
69
97
"DatabaseSection": {
70
98
"description": "Database configuration section (application DTO)\n\nMirrors the domain `DatabaseConfig` enum but at the application layer.\nSupports both `SQLite` and `MySQL` database backends.\n\n# Examples\n\n```json\n{\n\"driver\": \"sqlite3\",\n\"database_name\": \"tracker.db\"\n}\n```\n\n```json\n{\n\"driver\": \"mysql\",\n\"host\": \"localhost\",\n\"port\": 3306,\n\"database_name\": \"tracker\",\n\"username\": \"tracker_user\",\n\"password\": \"secure_password\"\n}\n```",
71
99
"oneOf": [
@@ -237,12 +265,15 @@
237
265
]
238
266
},
239
267
"HttpApiSection": {
268
+
"description": "HTTP API configuration section (Application DTO)\n\nThis is a Data Transfer Object that uses primitive types (`String`) for\nJSON deserialization. It converts to the domain type `HttpApiConfig` via\nthe `TryFrom` trait, which delegates validation to the domain layer.\n\n# Responsibility Split\n\n- **This DTO**: Parse strings into typed values (`SocketAddr`, `DomainName`)\n- **Domain type**: Enforce business invariants (port != 0, TLS requires domain, etc.)\n\n# Usage\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::tracker::HttpApiSection;\nuse torrust_tracker_deployer_lib::domain::tracker::HttpApiConfig;\n\nlet section = HttpApiSection {\n bind_address: \"0.0.0.0:1212\".to_string(),\n admin_token: \"MyToken\".to_string(),\n domain: None,\n use_tls_proxy: None,\n};\n\nlet config: HttpApiConfig = section.try_into()?;\n# Ok::<(), Box<dyn std::error::Error>>(())\n```\n\n# JSON Example\n\n```json\n{\n \"bind_address\": \"0.0.0.0:1212\",\n \"admin_token\": \"MyAccessToken\",\n \"domain\": \"api.example.com\",\n \"use_tls_proxy\": true\n}\n```",
240
269
"type": "object",
241
270
"properties": {
242
271
"admin_token": {
272
+
"description": "Admin token as plain string (at DTO boundary)\n\nConverted to `ApiToken` (secrecy-wrapped) in domain layer.",
243
273
"type": "string"
244
274
},
245
275
"bind_address": {
276
+
"description": "Bind address as string (e.g., \"0.0.0.0:1212\")\n\nParsed to `SocketAddr` during conversion.",
246
277
"type": "string"
247
278
},
248
279
"domain": {
@@ -337,7 +368,7 @@
337
368
]
338
369
},
339
370
"ProviderSection": {
340
-
"description": "Provider-specific configuration section\n\nEach variant contains the configuration fields specific to that provider\nusing **raw primitives** (`String`) for JSON deserialization.\n\nThis is a tagged enum that deserializes based on the `\"provider\"` field in JSON.\n\n# Conversion\n\nUse `to_provider_config()` to validate and convert to domain types.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::{\n ProviderSection, LxdProviderSection\n};\n\nlet section = ProviderSection::Lxd(LxdProviderSection {\n profile_name: \"torrust-profile-dev\".to_string(),\n});\n\nlet config= section.to_provider_config().unwrap();\nassert_eq!(config.provider_name(), \"lxd\");\n```",
371
+
"description": "Provider-specific configuration section\n\nEach variant contains the configuration fields specific to that provider\nusing **raw primitives** (`String`) for JSON deserialization.\n\nThis is a tagged enum that deserializes based on the `\"provider\"` field in JSON.\n\n# Conversion\n\nUse `try_into()` or `ProviderConfig::try_from()` to validate and convert to domain types.\n\n# Examples\n\n```rust\nuse torrust_tracker_deployer_lib::application::command_handlers::create::config::{\n ProviderSection, LxdProviderSection\n};\nuse torrust_tracker_deployer_lib::domain::provider::ProviderConfig;\nuse std::convert::TryInto;\n\nlet section = ProviderSection::Lxd(LxdProviderSection {\n profile_name: \"torrust-profile-dev\".to_string(),\n});\n\nlet config: ProviderConfig = section.try_into().unwrap();\nassert_eq!(config.provider_name(), \"lxd\");\n```",
0 commit comments