Files
shopdb-flask/plugins/backups/frontend/views/BackupsSettings.vue

158 lines
5.5 KiB
Vue

<template>
<div>
<div class="page-header">
<h2>Backups</h2>
</div>
<div class="section-card">
<div class="setting-group">
<h3>Collection</h3>
<p class="setting-description">
How often a PC attempts to capture the config it backs up. GE-Enforce runs every
cycle, so this is what stops a bay collecting several times a day. A capture that
finds nothing changed writes no revision - it only records that the check happened.
</p>
<div class="setting-row">
<label>
<span>Interval (hours)</span>
<input type="number" min="1" v-model="values.backups_intervalhours"
@blur="save('backups_intervalhours')" :disabled="saving">
<small class="input-hint">Default 24. Read by the fleet from /api/settings/public.</small>
</label>
</div>
<div class="setting-row">
<label>
<span>Share root (UNC)</span>
<input type="text" v-model="values.backups_shareroot"
placeholder="\\fileserver\shopfloor\backup"
@blur="save('backups_shareroot')" :disabled="saving">
<small class="input-hint">
Where opaque backups (part-marker snapshots) are written by the collecting PC.
Blank until the site sets it - no default is right at more than one site.
Advisory: each revision records the path it was actually written to.
</small>
</label>
</div>
</div>
</div>
<div class="section-card">
<div class="setting-group">
<h3>Health</h3>
<p class="setting-description">
A backup is judged on when it was last CONFIRMED, not when it last changed. An
unchanged config writes no revision, so a machine stable for months is healthy -
the one to worry about is the machine whose backup stopped running.
</p>
<div class="setting-row">
<label>
<span>Stale after (days)</span>
<input type="number" min="0" v-model="values.backups_staledays"
@blur="save('backups_staledays')" :disabled="saving">
<small class="input-hint">
Default 3. Drives the fleet Backup badge and the dashboard card. 0 disables both.
</small>
</label>
</div>
</div>
</div>
<div class="section-card">
<div class="setting-group">
<h3>Retention</h3>
<p class="setting-description">
Both off by default, and dedup already keeps growth low - a revision appears only
when a config really changed. Retention exists for a value that flaps. The newest
and oldest revision of a chain are never pruned.
</p>
<p class="setting-description">
<strong>Note:</strong> pruning removes database revisions only. Files written to the
share are not deleted, so enabling this on a share-backed kind leaves snapshot
directories with no revision pointing at them.
</p>
<div class="setting-row">
<label>
<span>Keep at most (revisions per chain)</span>
<input type="number" min="0" v-model="values.backups_retentioncount"
@blur="save('backups_retentioncount')" :disabled="saving">
<small class="input-hint">0 = keep everything.</small>
</label>
</div>
<div class="setting-row">
<label>
<span>Also drop older than (days)</span>
<input type="number" min="0" v-model="values.backups_retentiondays"
@blur="save('backups_retentiondays')" :disabled="saving">
<small class="input-hint">0 = no age limit.</small>
</label>
</div>
</div>
</div>
<div v-if="error" class="error-message">{{ error }}</div>
<div v-if="success" class="settings-success">{{ success }}</div>
</div>
</template>
<script setup>
// The backups plugin ships five settings and, until now, no page for any of
// them - they were reachable only through the API or a flask shell on the
// server. The core settings catalog is hardcoded and cannot be extended by a
// plugin, so this page is contributed the ADR-010 way: a settings card from
// get_settings_cards plus a route here, both of which disappear when the
// plugin is disabled.
import { ref, reactive, onMounted } from 'vue'
import { settingsApi } from '@/api'
import { apiError } from '@/utils/apiError'
const KEYS = [
'backups_intervalhours',
'backups_shareroot',
'backups_staledays',
'backups_retentioncount',
'backups_retentiondays',
]
const values = reactive({})
const saving = ref(false)
const error = ref('')
const success = ref('')
async function load() {
try {
const response = await settingsApi.list()
const rows = response.data?.data || response.data || []
// The list endpoint returns every setting; pick out ours rather than
// fetching five times.
for (const key of KEYS) {
const row = rows.find(item => item.key === key)
values[key] = row ? row.value : ''
}
} catch (err) {
error.value = apiError(err, 'Could not load backup settings')
}
}
async function save(key) {
saving.value = true
error.value = ''
success.value = ''
try {
await settingsApi.update(key, values[key])
success.value = 'Saved'
setTimeout(() => { success.value = '' }, 2000)
} catch (err) {
error.value = apiError(err, `Could not save ${key}`)
} finally {
saving.value = false
}
}
onMounted(load)
</script>