Add backups with S3, daily cost push, automation templates and a browser tool
- Daily backups (database + config) into <data_dir>/backups, keep N, optional upload to any S3-compatible bucket (SigV4, bucket pruning); settings panel with run-now, list and download - Daily cost summary push at 20:00 (opt-in per user, preview button) - Automations from templates (dependency check, security review, test health, daily summary, TODO report, docs freshness) via POST /jobs/create - browser tool: headless Chromium over CDP – click, type, keys, waits, JavaScript, navigation, screenshots, console errors; requests to private network addresses are blocked (sub-resources and redirects included) - Secrets masked in the config view: S3 secret key, metrics token - Replace the remaining text arrows/refresh glyphs with SVG icons Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
873447ed17
commit
01c1484033
25 files changed
+1720
-47
No files matched your search
+43
-2
@@ -30,10 +30,49 @@ pub struct Config {
|
||||
pub sandbox: SandboxConfig,
|
||||
pub search: SearchConfig,
|
||||
pub lsp: LspConfig,
|
||||
pub backup: BackupConfig,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub hooks: Vec<HookConfig>,
|
||||
}
|
||||
|
||||
/// Automatic daily backups (database + config) on disk and optionally in S3.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct BackupConfig {
|
||||
pub enabled: bool,
|
||||
/// Local hour (0-23) of the daily run.
|
||||
pub hour: u32,
|
||||
/// Number of local backups to keep.
|
||||
pub keep: usize,
|
||||
/// Directory for the zip files (default: <data_dir>/backups).
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub dir: String,
|
||||
pub s3: S3Config,
|
||||
}
|
||||
|
||||
impl Default for BackupConfig {
|
||||
fn default() -> Self {
|
||||
Self { enabled: true, hour: 3, keep: 30, dir: String::new(), s3: S3Config::default() }
|
||||
}
|
||||
}
|
||||
|
||||
/// S3-compatible bucket (AWS, MinIO, Garage, Backblaze B2, Hetzner, …).
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct S3Config {
|
||||
/// e.g. https://s3.eu-central-1.amazonaws.com or https://minio.example.com; empty = no upload.
|
||||
pub endpoint: String,
|
||||
pub region: String,
|
||||
pub bucket: String,
|
||||
/// Key prefix, e.g. "codecraft/".
|
||||
pub prefix: String,
|
||||
pub access_key: String,
|
||||
/// Supports ${ENV_VAR}.
|
||||
pub secret_key: String,
|
||||
/// Old backups beyond this number are deleted from the bucket (0 = keep all).
|
||||
pub keep: usize,
|
||||
}
|
||||
|
||||
/// A shell command run on an agent event (see hooks.rs).
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct HookConfig {
|
||||
@@ -560,8 +599,10 @@ pub fn redacted_json(cfg: &Config) -> serde_json::Value {
|
||||
if let Some(t) = v.pointer_mut("/images/admin_key") {
|
||||
mask(t);
|
||||
}
|
||||
if let Some(t) = v.pointer_mut("/search/api_key") {
|
||||
mask(t);
|
||||
for p in ["/search/api_key", "/backup/s3/secret_key", "/server/metrics_token"] {
|
||||
if let Some(t) = v.pointer_mut(p) {
|
||||
mask(t);
|
||||
}
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user