1.1.3 - Bug fixes & updates to the automation scheduler
This commit is contained in:
@@ -172,7 +172,7 @@
|
||||
class={`flex items-center px-3 py-2 text-xs ${collapsed ? "justify-center" : "justify-between"}`}
|
||||
>
|
||||
{#if !collapsed}
|
||||
<Badge className="text-text-secondary">Version: v1.1.2</Badge>
|
||||
<Badge className="text-text-secondary">Version: v1.1.3</Badge>
|
||||
{:else}
|
||||
<Badge className="text-text-secondary">v</Badge>
|
||||
{/if}
|
||||
|
||||
@@ -451,6 +451,17 @@ export async function runAutomationRule(ruleId, dryRun = false) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/automation/logs - Get automation run logs
|
||||
* Query params: rule_id (optional), limit (default 100)
|
||||
* Returns: { success, logs: [...] }
|
||||
*/
|
||||
export async function getAutomationLogs(ruleId = null, limit = 100) {
|
||||
const params = new URLSearchParams({ limit })
|
||||
if (ruleId) params.set('rule_id', ruleId)
|
||||
return apiFetch(`/automation/logs?${params}`)
|
||||
}
|
||||
|
||||
// ============ SUGGESTED MATCHES API ============
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,14 +14,25 @@
|
||||
? [...rule.target_folders]
|
||||
: ["/media/movies"];
|
||||
|
||||
$: if (rule) {
|
||||
name = rule.name || "";
|
||||
schedule = rule.schedule || "0 3 * * SUN";
|
||||
enabled = rule.enabled ?? true;
|
||||
patterns = rule.patterns ? [...rule.patterns] : [];
|
||||
targetFolders = rule.target_folders ? [...rule.target_folders] : [];
|
||||
// Re-sync form whenever the incoming rule prop changes (edit ↔ create toggle).
|
||||
$: {
|
||||
if (rule) {
|
||||
name = rule.name || "";
|
||||
schedule = rule.schedule || "0 3 * * SUN";
|
||||
enabled = rule.enabled ?? true;
|
||||
patterns = rule.patterns ? [...rule.patterns] : [];
|
||||
targetFolders = rule.target_folders ? [...rule.target_folders] : [];
|
||||
} else {
|
||||
name = "";
|
||||
schedule = "0 3 * * SUN";
|
||||
enabled = true;
|
||||
patterns = ["YTS", "YIFY"];
|
||||
targetFolders = ["/media/movies"];
|
||||
}
|
||||
}
|
||||
|
||||
let formError = "";
|
||||
|
||||
function addPattern() {
|
||||
patterns = [...patterns, ""];
|
||||
}
|
||||
@@ -49,12 +60,25 @@
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
formError = "";
|
||||
const trimmedName = name.trim();
|
||||
const trimmedSchedule = schedule.trim();
|
||||
|
||||
if (!trimmedName) {
|
||||
formError = "Rule name is required.";
|
||||
return;
|
||||
}
|
||||
if (!trimmedSchedule) {
|
||||
formError = "Schedule is required.";
|
||||
return;
|
||||
}
|
||||
|
||||
const cleanPatterns = patterns.map((p) => p.trim()).filter(Boolean);
|
||||
const cleanFolders = targetFolders.map((f) => f.trim()).filter(Boolean);
|
||||
|
||||
onSave({
|
||||
name: name.trim(),
|
||||
schedule: schedule.trim(),
|
||||
name: trimmedName,
|
||||
schedule: trimmedSchedule,
|
||||
enabled,
|
||||
patterns: cleanPatterns,
|
||||
target_folders: cleanFolders,
|
||||
@@ -200,6 +224,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if formError}
|
||||
<p class="text-[12px] text-red-400">{formError}</p>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:justify-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
let showForm = false;
|
||||
let dryRunByRule = {};
|
||||
let running = {};
|
||||
// Last run result per rule id: { files_scanned, files_modified, removed_lines, dry_run, errors }
|
||||
let lastRunResult = {};
|
||||
|
||||
async function loadRules() {
|
||||
loading = true;
|
||||
@@ -108,9 +110,11 @@
|
||||
rule.id,
|
||||
dryRunByRule[rule.id] === true,
|
||||
);
|
||||
lastRunResult[rule.id] = result;
|
||||
lastRunResult = { ...lastRunResult };
|
||||
const label = result.dry_run ? "Dry run" : "Run";
|
||||
addToast({
|
||||
message: `${label} complete. ${result.files_modified}/${result.files_scanned} modified.`,
|
||||
message: `${label} complete. ${result.files_modified}/${result.files_scanned} modified, ${result.removed_lines} lines removed.`,
|
||||
tone: "success",
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -123,6 +127,21 @@
|
||||
running = { ...running };
|
||||
}
|
||||
}
|
||||
|
||||
function formatNextRun(isoString) {
|
||||
if (!isoString) return null;
|
||||
try {
|
||||
const d = new Date(isoString);
|
||||
return d.toLocaleString(undefined, {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-8">
|
||||
@@ -198,9 +217,15 @@
|
||||
Schedule: <span class="font-mono">{rule.schedule}</span>
|
||||
</div>
|
||||
<div class="text-[12px] text-text-tertiary">
|
||||
Targets: {rule.target_folders.length} folder
|
||||
{rule.target_folders.length === 1 ? "" : "s"}
|
||||
Targets: {rule.target_folders.length} folder{rule.target_folders.length === 1 ? "" : "s"}
|
||||
</div>
|
||||
{#if rule.next_run_at && formatNextRun(rule.next_run_at)}
|
||||
<div class="text-[11px] text-text-tertiary">
|
||||
Next run: <span class="text-text-secondary">{formatNextRun(rule.next_run_at)}</span>
|
||||
</div>
|
||||
{:else if rule.enabled}
|
||||
<div class="text-[11px] text-text-tertiary">Next run: not scheduled</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
@@ -272,6 +297,26 @@
|
||||
<Play class="h-4 w-4" />
|
||||
{running[rule.id] ? "Running..." : "Run now"}
|
||||
</Button>
|
||||
|
||||
{#if lastRunResult[rule.id]}
|
||||
{@const res = lastRunResult[rule.id]}
|
||||
<div class="mt-3 rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-[11px] text-text-tertiary space-y-1">
|
||||
<div class="font-medium text-text-secondary">
|
||||
{res.dry_run ? "Dry run" : "Run"} result
|
||||
</div>
|
||||
<div>
|
||||
{res.files_modified} / {res.files_scanned} files modified · {res.removed_lines} lines removed
|
||||
</div>
|
||||
{#if res.errors && res.errors.length > 0}
|
||||
<div class="mt-1 space-y-0.5">
|
||||
<div class="text-red-400">{res.errors.length} error{res.errors.length === 1 ? "" : "s"}:</div>
|
||||
{#each res.errors as err}
|
||||
<div class="font-mono text-red-300 truncate" title={err}>{err}</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user