<!-- htmlhint doctype-first:false -->
<div class="space-y-6">
<!-- Maintenance Header -->
<div class="bg-gradient-to-r from-amber-500 to-orange-600 rounded-lg shadow-lg p-6 text-white">
<h2 class="text-2xl font-bold mb-2 flex items-center">
<svg
class="h-8 w-8 mr-3"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
Platform Maintenance
</h2>
<p class="text-amber-100">
Administrative tools for platform maintenance and data management. These operations are restricted to platform administrators.
</p>
</div>
<!-- Metrics Maintenance Card -->
{% if payload.settings.metrics_cleanup_enabled or payload.settings.metrics_rollup_enabled %}
<div class="bg-white rounded-lg shadow p-6 dark:bg-gray-800" x-data="{
cleanupLoading: false,
rollupLoading: false,
cleanupResult: null,
rollupResult: null,
showCleanupConfirm: false,
showRollupConfirm: false,
retentionDays: {{ payload.settings.metrics_retention_days | default(7) }},
deleteAll: false,
rollupHoursBack: 1,
includeRollupData: true
}">
<h3 class="text-lg font-medium mb-4 dark:text-gray-200 flex items-center">
<svg
class="h-6 w-6 mr-2 text-indigo-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
></path>
</svg>
Metrics Maintenance
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
Manage metrics data retention and aggregation. Cleanup removes old raw metrics,
rollup creates hourly summaries for efficient historical queries.
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Cleanup Section -->
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
<h4 class="text-sm font-medium mb-3 dark:text-gray-200 flex items-center">
<svg class="h-4 w-4 mr-2 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Metrics Cleanup
</h4>
<!-- Retention Days Input -->
<div class="mb-3">
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
Delete metrics older than:
</label>
<div class="flex items-center gap-2">
<input
type="number"
x-model="retentionDays"
:disabled="deleteAll"
min="0"
max="365"
class="w-20 px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded dark:bg-gray-800 dark:text-gray-200 disabled:opacity-50"
/>
<span class="text-xs text-gray-500 dark:text-gray-400">days</span>
</div>
</div>
<!-- Delete All Checkbox -->
<div class="mb-3">
<label class="flex items-center text-xs text-gray-600 dark:text-gray-400 cursor-pointer">
<input
type="checkbox"
x-model="deleteAll"
@change="if(deleteAll) retentionDays = 0"
class="mr-2 rounded border-gray-300 dark:border-gray-600 text-red-600 focus:ring-red-500"
/>
<span class="font-medium text-red-600 dark:text-red-400">Delete ALL metrics (ignores retention)</span>
</label>
</div>
<!-- Include Rollup Data Checkbox -->
<div class="mb-3">
<label class="flex items-center text-xs text-gray-600 dark:text-gray-400 cursor-pointer">
<input
type="checkbox"
x-model="includeRollupData"
class="mr-2 rounded border-gray-300 dark:border-gray-600 text-red-600 focus:ring-red-500"
/>
<span>Also delete hourly rollup data</span>
</label>
</div>
<!-- Run Cleanup Button -->
<button
@click="showCleanupConfirm = true"
:disabled="cleanupLoading"
class="w-full inline-flex items-center justify-center px-4 py-2 bg-red-600 hover:bg-red-700 disabled:bg-red-400 text-white text-sm font-medium rounded-md shadow-sm transition-colors"
>
<template x-if="cleanupLoading">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</template>
<span x-text="cleanupLoading ? 'Deleting...' : (deleteAll ? 'Delete All Metrics' : 'Run Cleanup')"></span>
</button>
<!-- Cleanup Confirmation Dialog -->
<template x-if="showCleanupConfirm">
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" @click.self="showCleanupConfirm = false">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 max-w-md mx-4">
<div class="flex items-center mb-4">
<svg class="h-6 w-6 text-red-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100">Confirm Metrics Cleanup</h4>
</div>
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
<template x-if="deleteAll">
<p class="text-red-600 dark:text-red-400 font-medium">
WARNING: This will permanently delete ALL metrics data!
</p>
</template>
<template x-if="!deleteAll">
<p>
This will delete all metrics older than <span class="font-medium" x-text="retentionDays"></span> days.
</p>
</template>
<p class="mt-2" x-show="includeRollupData">
Hourly rollup data will also be deleted.
</p>
<p class="mt-2 font-medium">This action cannot be undone.</p>
</div>
<div class="flex justify-end gap-3">
<button
@click="showCleanupConfirm = false"
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-md"
>
Cancel
</button>
<button
@click="
showCleanupConfirm = false;
cleanupLoading = true;
cleanupResult = null;
fetch('{{ root_path }}/api/metrics/cleanup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + (document.cookie.match(/jwt_token=([^;]+)/)?.[1] || '')
},
body: JSON.stringify({
retention_days: deleteAll ? 0 : retentionDays,
include_rollup: includeRollupData
})
})
.then(r => r.json())
.then(data => { cleanupResult = data; cleanupLoading = false; })
.catch(e => { cleanupResult = {error: e.message}; cleanupLoading = false; })
"
class="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md"
>
Yes, Delete
</button>
</div>
</div>
</div>
</template>
<!-- Cleanup Result -->
<template x-if="cleanupResult">
<div class="mt-3 p-2 rounded text-xs" :class="cleanupResult.error ? 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300' : 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300'">
<template x-if="!cleanupResult.error">
<div>
<span class="font-medium">Deleted:</span> <span x-text="cleanupResult.total_deleted"></span> records
<span class="text-gray-500 dark:text-gray-400 ml-2">(<span x-text="cleanupResult.duration_seconds?.toFixed(2)"></span>s)</span>
</div>
</template>
<template x-if="cleanupResult.error">
<span x-text="cleanupResult.error || cleanupResult.detail"></span>
</template>
</div>
</template>
</div>
<!-- Rollup Section -->
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
<h4 class="text-sm font-medium mb-3 dark:text-gray-200 flex items-center">
<svg class="h-4 w-4 mr-2 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
Metrics Rollup
</h4>
<!-- Hours Back Input -->
<div class="mb-3">
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">
Process metrics from last:
</label>
<div class="flex items-center gap-2">
<select
x-model="rollupHoursBack"
class="px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded dark:bg-gray-800 dark:text-gray-200"
>
<option value="1">1 hour</option>
<option value="4">4 hours</option>
<option value="12">12 hours</option>
<option value="24">24 hours (1 day)</option>
<option value="72">72 hours (3 days)</option>
<option value="168">168 hours (7 days)</option>
<option value="720">720 hours (30 days)</option>
<option value="2160">2160 hours (90 days)</option>
<option value="8760">8760 hours (365 days)</option>
</select>
</div>
</div>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
Aggregates raw metrics into hourly summaries with percentiles (p50, p95, p99).
</p>
<!-- Run Rollup Button -->
<button
@click="showRollupConfirm = true"
:disabled="rollupLoading"
class="w-full inline-flex items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white text-sm font-medium rounded-md shadow-sm transition-colors"
>
<template x-if="rollupLoading">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</template>
<span x-text="rollupLoading ? 'Processing...' : 'Run Rollup Now'"></span>
</button>
<!-- Rollup Confirmation Dialog -->
<template x-if="showRollupConfirm">
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" @click.self="showRollupConfirm = false">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 max-w-md mx-4">
<div class="flex items-center mb-4">
<svg class="h-6 w-6 text-blue-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
</svg>
<h4 class="text-lg font-medium text-gray-900 dark:text-gray-100">Confirm Metrics Rollup</h4>
</div>
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
<p>
This will aggregate raw metrics from the last <span class="font-medium" x-text="rollupHoursBack"></span> hours into hourly summaries.
</p>
<p class="mt-2">
Existing rollups for those hours will be updated with current data.
</p>
</div>
<div class="flex justify-end gap-3">
<button
@click="showRollupConfirm = false"
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-md"
>
Cancel
</button>
<button
@click="
showRollupConfirm = false;
rollupLoading = true;
rollupResult = null;
fetch('{{ root_path }}/api/metrics/rollup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + (document.cookie.match(/jwt_token=([^;]+)/)?.[1] || '')
},
body: JSON.stringify({hours_back: parseInt(rollupHoursBack)})
})
.then(r => r.json())
.then(data => { rollupResult = data; rollupLoading = false; })
.catch(e => { rollupResult = {error: e.message}; rollupLoading = false; })
"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md"
>
Yes, Run Rollup
</button>
</div>
</div>
</div>
</template>
<!-- Rollup Result -->
<template x-if="rollupResult">
<div class="mt-3 p-2 rounded text-xs" :class="rollupResult.error ? 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300' : 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300'">
<template x-if="!rollupResult.error">
<div>
<span class="font-medium">New:</span> <span x-text="rollupResult.total_rollups_created"></span>,
<span class="font-medium">Updated:</span> <span x-text="rollupResult.total_rollups_updated"></span> rollups
<span class="text-gray-500 dark:text-gray-400 ml-2">(<span x-text="rollupResult.duration_seconds?.toFixed(2)"></span>s)</span>
</div>
</template>
<template x-if="rollupResult.error">
<span x-text="rollupResult.error || rollupResult.detail"></span>
</template>
</div>
</template>
</div>
</div>
<!-- Info Notice -->
<div class="mt-4 flex items-start bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3">
<svg class="h-5 w-5 text-blue-600 dark:text-blue-400 mr-2 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div class="text-sm text-blue-700 dark:text-blue-300">
Both operations run automatically in the background. Manual triggers are for immediate execution.
</div>
</div>
</div>
{% else %}
<!-- Metrics maintenance disabled -->
<div class="bg-white rounded-lg shadow p-6 dark:bg-gray-800">
<h3 class="text-lg font-medium mb-4 dark:text-gray-200 flex items-center">
<svg
class="h-6 w-6 mr-2 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
></path>
</svg>
Metrics Maintenance
</h3>
<div class="flex items-start bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg p-4">
<svg class="h-5 w-5 text-gray-500 dark:text-gray-400 mr-3 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<div class="text-sm text-gray-600 dark:text-gray-400">
<p class="font-medium text-gray-700 dark:text-gray-300">Metrics maintenance is disabled</p>
<p class="mt-1">
To enable metrics cleanup and rollup, set <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded">METRICS_CLEANUP_ENABLED=true</code>
and/or <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded">METRICS_ROLLUP_ENABLED=true</code> in your environment.
</p>
</div>
</div>
</div>
{% endif %}
<!-- Future Maintenance Tasks Placeholder -->
<div class="bg-white rounded-lg shadow p-6 dark:bg-gray-800">
<h3 class="text-lg font-medium mb-4 dark:text-gray-200 flex items-center">
<svg
class="h-6 w-6 mr-2 text-indigo-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"
></path>
</svg>
System Health
</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">Database</div>
<div class="text-lg font-semibold text-green-600 dark:text-green-400 flex items-center mt-1">
<svg class="h-5 w-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Healthy
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">Cache</div>
<div class="text-lg font-semibold text-green-600 dark:text-green-400 flex items-center mt-1">
<svg class="h-5 w-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Operational
</div>
</div>
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
<div class="text-sm font-medium text-gray-500 dark:text-gray-400">Background Jobs</div>
<div class="text-lg font-semibold text-green-600 dark:text-green-400 flex items-center mt-1">
<svg class="h-5 w-5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
Running
</div>
</div>
</div>
</div>
</div>