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
@@ -5,6 +5,7 @@ import { navigate } from "../router";
|
||||
import { confirmDialog, toast } from "../dialogs";
|
||||
import { Icon } from "./Icon";
|
||||
import { Spinner } from "./Activity";
|
||||
import { JobTemplateDialog } from "./JobTemplateDialog";
|
||||
|
||||
interface JobInfo {
|
||||
id: string;
|
||||
@@ -28,6 +29,7 @@ export function Jobs({ onMenu }: { onMenu: () => void }) {
|
||||
const [path, setPath] = useState("");
|
||||
const [busy, setBusy] = useState<string>();
|
||||
const [hook, setHook] = useState<{ id: string; url: string; secret: string }>();
|
||||
const [templates, setTemplates] = useState(false);
|
||||
|
||||
const reload = () =>
|
||||
get<JobInfo[]>("/jobs")
|
||||
@@ -64,6 +66,12 @@ export function Jobs({ onMenu }: { onMenu: () => void }) {
|
||||
<div className="page">
|
||||
<div className="page-inner" style={{ maxWidth: 980 }}>
|
||||
<div className="notice">{t("jobs.intro")}</div>
|
||||
<div className="row" style={{ margin: "12px 0" }}>
|
||||
<button className="primary" onClick={() => setTemplates(true)}>
|
||||
<Icon name="plus" /> {t("jobs.fromTemplate")}
|
||||
</button>
|
||||
</div>
|
||||
{templates && <JobTemplateDialog onClose={() => setTemplates(false)} onCreated={() => void reload()} />}
|
||||
{!jobs && <div className="empty"><Spinner /></div>}
|
||||
{jobs?.length === 0 && <div className="empty">{t("jobs.none")}</div>}
|
||||
{jobs?.map((j) => (
|
||||
|
||||
Reference in new issue
Block a user