Add manual-only backup schedules
This commit is contained in:
@@ -1 +0,0 @@
|
||||
56f07ac0a18bf9067c8cb38ef6e5cbf69d52a2313e37f32f87b619268a47d6e8 urbm-2026.06.15.r018-x86_64-1.txz
|
||||
BIN
Binary file not shown.
@@ -0,0 +1 @@
|
||||
89f13497e0503b91602af00343fb15cd897e775189e63fbd6af4e0c302ff06d4 urbm-2026.06.15.r019-x86_64-1.txz
|
||||
Vendored
+6
-2
@@ -2,13 +2,17 @@
|
||||
<!DOCTYPE PLUGIN [
|
||||
<!ENTITY name "urbm">
|
||||
<!ENTITY author "Michael Roll">
|
||||
<!ENTITY version "2026.06.15.r018">
|
||||
<!ENTITY version "2026.06.15.r019">
|
||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm.plg">
|
||||
<!ENTITY packageURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm-&version;-x86_64-1.txz">
|
||||
<!ENTITY packageSHA256 "56f07ac0a18bf9067c8cb38ef6e5cbf69d52a2313e37f32f87b619268a47d6e8">
|
||||
<!ENTITY packageSHA256 "89f13497e0503b91602af00343fb15cd897e775189e63fbd6af4e0c302ff06d4">
|
||||
]>
|
||||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/URBM/issues" icon="urbm.png">
|
||||
<CHANGES>
|
||||
### 2026.06.15.r019
|
||||
- Add a clearly labeled manual-only schedule mode for every backup and Rsync job.
|
||||
- Keep manual-only jobs enabled for one-click starts while excluding them from scheduled and catch-up runs.
|
||||
|
||||
### 2026.06.15.r018
|
||||
- Run a write-free Rsync preflight to estimate the actual transfer size before copying.
|
||||
- Abort Rsync jobs when the estimated transfer exceeds the free destination capacity.
|
||||
|
||||
+5
-1
@@ -2,13 +2,17 @@
|
||||
<!DOCTYPE PLUGIN [
|
||||
<!ENTITY name "urbm">
|
||||
<!ENTITY author "Michael Roll">
|
||||
<!ENTITY version "2026.06.15.r018">
|
||||
<!ENTITY version "2026.06.15.r019">
|
||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm.plg">
|
||||
<!ENTITY packageURL "https://git.casaderoll.de/michael/URBM/raw/branch/main/dist/urbm-&version;-x86_64-1.txz">
|
||||
<!ENTITY packageSHA256 "REPLACE_DURING_RELEASE">
|
||||
]>
|
||||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" min="7.0.0" support="https://git.casaderoll.de/michael/URBM/issues" icon="urbm.png">
|
||||
<CHANGES>
|
||||
### 2026.06.15.r019
|
||||
- Add a clearly labeled manual-only schedule mode for every backup and Rsync job.
|
||||
- Keep manual-only jobs enabled for one-click starts while excluding them from scheduled and catch-up runs.
|
||||
|
||||
### 2026.06.15.r018
|
||||
- Run a write-free Rsync preflight to estimate the actual transfer size before copying.
|
||||
- Abort Rsync jobs when the estimated transfer exceeds the free destination capacity.
|
||||
|
||||
+4
-4
@@ -9,12 +9,12 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore"
|
||||
<?php
|
||||
$pluginRoot = '/plugins/urbm';
|
||||
?>
|
||||
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/urbm.css?v=20260615r018">
|
||||
<link rel="stylesheet" href="<?= $pluginRoot ?>/assets/urbm.css?v=20260615r019">
|
||||
<div id="urbm-app" data-api="<?= $pluginRoot ?>/api.php">
|
||||
<header class="bu-header">
|
||||
<div class="bu-brand">
|
||||
<img class="bu-logo" src="<?= $pluginRoot ?>/images/urbm.png?v=20260615r018" alt="URBM-Logo">
|
||||
<div><h1>URBM <span class="bu-version">2026.06.15.r018</span></h1><p>Restic Backup Manager für Unraid</p></div>
|
||||
<img class="bu-logo" src="<?= $pluginRoot ?>/images/urbm.png?v=20260615r019" alt="URBM-Logo">
|
||||
<div><h1>URBM <span class="bu-version">2026.06.15.r019</span></h1><p>Restic Backup Manager für Unraid</p></div>
|
||||
</div>
|
||||
<div id="bu-health" class="bu-health" tabindex="0" data-tooltip="Zeigt, ob die URBM-Hintergrundkomponente erreichbar ist. Beispiel: 'Daemon online' bedeutet, dass Jobs gestartet werden können.">Verbindung wird hergestellt...</div>
|
||||
</header>
|
||||
@@ -32,4 +32,4 @@ $pluginRoot = '/plugins/urbm';
|
||||
<div id="bu-tooltip" role="tooltip" aria-hidden="true"></div>
|
||||
</div>
|
||||
<script>window.URBM_CSRF = <?= json_encode($var['csrf_token'] ?? '') ?>;</script>
|
||||
<script src="<?= $pluginRoot ?>/assets/urbm-2026.06.15.r018.js"></script>
|
||||
<script src="<?= $pluginRoot ?>/assets/urbm-2026.06.15.r019.js"></script>
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
.bu-schedule-grid { display: grid; gap: 15px; grid-template-columns: minmax(240px, 2fr) minmax(150px, 1fr); }
|
||||
.bu-weekdays { align-items: center; display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
|
||||
.bu-weekdays[hidden] { display: none; }
|
||||
.bu-schedule-time[hidden], .bu-schedule-manual[hidden] { display: none !important; }
|
||||
.bu-weekdays > span { font-weight: 700; margin-right: 4px; }
|
||||
#urbm-app .bu-weekdays label { display: block; }
|
||||
#urbm-app .bu-weekdays label span { background: var(--bu-panel-alt); border: 1px solid var(--bu-border-strong); border-radius: 5px; cursor: pointer; display: block; min-width: 46px; padding: 9px 11px; text-align: center; }
|
||||
|
||||
+13
-4
@@ -193,7 +193,9 @@
|
||||
};
|
||||
const weekDays = [{value:'1',short:'Mo',name:'Montag'},{value:'2',short:'Di',name:'Dienstag'},{value:'3',short:'Mi',name:'Mittwoch'},{value:'4',short:'Do',name:'Donnerstag'},{value:'5',short:'Fr',name:'Freitag'},{value:'6',short:'Sa',name:'Samstag'},{value:'0',short:'So',name:'Sonntag'}];
|
||||
function parseSchedule(cron = '0 2 * * *') {
|
||||
const match = String(cron).trim().match(/^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+(.+)$/);
|
||||
const value=String(cron).trim();
|
||||
if(!value) return {mode:'manual',time:'02:00',days:[],cron:''};
|
||||
const match = value.match(/^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+(.+)$/);
|
||||
if (!match || Number(match[1]) > 59 || Number(match[2]) > 23) return {mode:'custom',time:'02:00',days:[],cron};
|
||||
const time = `${String(match[2]).padStart(2,'0')}:${String(match[1]).padStart(2,'0')}`;
|
||||
if (match[3] === '*') return {mode:'daily',time,days:[],cron};
|
||||
@@ -204,6 +206,7 @@
|
||||
}
|
||||
function scheduleCron(form) {
|
||||
const mode = form.get('scheduleMode');
|
||||
if (mode === 'manual') return '';
|
||||
if (mode === 'custom') return form.get('customCron');
|
||||
const [hour, minute] = String(form.get('scheduleTime') || '02:00').split(':');
|
||||
const dayField = mode === 'daily' ? '*' : mode === 'weekdays' ? '1-5' : form.getAll('scheduleDays').join(',');
|
||||
@@ -212,6 +215,7 @@
|
||||
}
|
||||
function scheduleLabel(cron) {
|
||||
const schedule = parseSchedule(cron);
|
||||
if (schedule.mode === 'manual') return 'Nur manuell';
|
||||
if (schedule.mode === 'custom') return cron || 'Manuell';
|
||||
if (schedule.mode === 'daily') return `Täglich um ${schedule.time} Uhr`;
|
||||
if (schedule.mode === 'weekdays') return `Mo–Fr um ${schedule.time} Uhr`;
|
||||
@@ -219,10 +223,11 @@
|
||||
return `${labels} um ${schedule.time} Uhr`;
|
||||
}
|
||||
function scheduleFields(job = {}) {
|
||||
const schedule = parseSchedule(job.schedule?.cron || '0 2 * * *');
|
||||
const modes = [['daily','Täglich'],['weekdays','Montag bis Freitag'],['selected','Ausgewählte Wochentage']];
|
||||
const cron=job.schedule&&Object.prototype.hasOwnProperty.call(job.schedule,'cron')?job.schedule.cron:'0 2 * * *';
|
||||
const schedule = parseSchedule(cron);
|
||||
const modes = [['manual','Nur manuell starten'],['daily','Täglich'],['weekdays','Montag bis Freitag'],['selected','Ausgewählte Wochentage']];
|
||||
if (schedule.mode === 'custom') modes.push(['custom','Bestehenden Cron-Zeitplan beibehalten']);
|
||||
return `<fieldset class="wide bu-fieldset bu-schedule"><legend>Backup-Zeitplan</legend><div class="bu-schedule-grid"><label>Tage<select name="scheduleMode">${modes.map(([value,label])=>`<option value="${value}" ${value===schedule.mode?'selected':''}>${label}</option>`).join('')}</select></label><label>Uhrzeit<input name="scheduleTime" type="time" required value="${esc(schedule.time)}"></label></div><div class="bu-weekdays" ${schedule.mode==='selected'?'':'hidden'}><span>Wochentage</span>${weekDays.map(day=>`<label><input type="checkbox" name="scheduleDays" value="${day.value}" ${schedule.days.includes(day.value)?'checked':''}><span>${day.short}</span></label>`).join('')}</div>${schedule.mode==='custom'?`<label class="bu-custom-cron">Bestehender Zeitplan<input name="customCron" readonly value="${esc(schedule.cron)}"></label>`:''}</fieldset>`;
|
||||
return `<fieldset class="wide bu-fieldset bu-schedule"><legend>Backup-Zeitplan</legend><div class="bu-schedule-grid"><label>Ausführung<select name="scheduleMode">${modes.map(([value,label])=>`<option value="${value}" ${value===schedule.mode?'selected':''}>${label}</option>`).join('')}</select></label><label class="bu-schedule-time" ${schedule.mode==='manual'?'hidden':''}>Uhrzeit<input name="scheduleTime" type="time" required value="${esc(schedule.time)}"></label></div><div class="bu-schedule-manual bu-inline-info" ${schedule.mode==='manual'?'':'hidden'}>Dieser Job wird nicht automatisch gestartet. Er kann jederzeit in der Job-Liste über <strong>Starten</strong> ausgeführt werden.</div><div class="bu-weekdays" ${schedule.mode==='selected'?'':'hidden'}><span>Wochentage</span>${weekDays.map(day=>`<label><input type="checkbox" name="scheduleDays" value="${day.value}" ${schedule.days.includes(day.value)?'checked':''}><span>${day.short}</span></label>`).join('')}</div>${schedule.mode==='custom'?`<label class="bu-custom-cron">Bestehender Zeitplan<input name="customCron" readonly value="${esc(schedule.cron)}"></label>`:''}</fieldset>`;
|
||||
}
|
||||
|
||||
function lineChart(runs, field, formatter, emptyText) {
|
||||
@@ -723,6 +728,10 @@
|
||||
if (e.target.name === 'scheduleMode') {
|
||||
const weekdays=e.target.closest('.bu-schedule')?.querySelector('.bu-weekdays');
|
||||
if (weekdays) weekdays.hidden=e.target.value!=='selected';
|
||||
const time=e.target.closest('.bu-schedule')?.querySelector('.bu-schedule-time');
|
||||
if(time) time.hidden=e.target.value==='manual';
|
||||
const manual=e.target.closest('.bu-schedule')?.querySelector('.bu-schedule-manual');
|
||||
if(manual) manual.hidden=e.target.value!=='manual';
|
||||
}
|
||||
if (e.target.name === 'type' && e.target.closest('#bu-job-form')) {
|
||||
state.backupSelections=[]; state.workloadSelections=[];
|
||||
|
||||
Reference in New Issue
Block a user