803 lines
27 KiB
Vue
803 lines
27 KiB
Vue
<template>
|
|
<div>
|
|
<LayoutsBreadcrumb />
|
|
|
|
<!-- Header Section -->
|
|
<rs-card class="mb-6">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-primary" name="ic:outline-storage"></Icon>
|
|
<h1 class="text-xl font-bold text-primary">Queue Persistence Configuration</h1>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<div class="flex items-center">
|
|
<div class="w-2 h-2 bg-green-500 rounded-full mr-2 animate-pulse"></div>
|
|
<span class="text-sm text-gray-600">Persistence Active</span>
|
|
</div>
|
|
<rs-button variant="outline" size="sm" @click="testPersistence">
|
|
<Icon class="mr-1" name="ic:outline-bug-report"></Icon>
|
|
Test Recovery
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<p class="text-gray-600">
|
|
Configure queue data persistence to ensure notifications survive system restarts and failures.
|
|
Critical for maintaining queue integrity and preventing message loss during system maintenance.
|
|
</p>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Persistence Status Overview -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
|
|
<rs-card
|
|
v-for="(metric, index) in persistenceMetrics"
|
|
:key="index"
|
|
class="transition-all duration-300 hover:shadow-lg"
|
|
>
|
|
<div class="pt-5 pb-3 px-5 flex items-center gap-4">
|
|
<div
|
|
class="p-4 flex justify-center items-center rounded-2xl"
|
|
:class="metric.bgColor"
|
|
>
|
|
<Icon class="text-2xl" :class="metric.iconColor" :name="metric.icon"></Icon>
|
|
</div>
|
|
<div class="flex-1 truncate">
|
|
<span class="block font-bold text-2xl leading-tight" :class="metric.valueColor">
|
|
{{ metric.value }}
|
|
</span>
|
|
<span class="text-sm font-medium text-gray-600">
|
|
{{ metric.title }}
|
|
</span>
|
|
<div class="flex items-center mt-1" v-if="metric.status">
|
|
<div
|
|
class="w-2 h-2 rounded-full mr-1"
|
|
:class="{
|
|
'bg-green-500': metric.status === 'healthy',
|
|
'bg-yellow-500': metric.status === 'warning',
|
|
'bg-red-500': metric.status === 'error'
|
|
}"
|
|
></div>
|
|
<span class="text-xs capitalize" :class="{
|
|
'text-green-600': metric.status === 'healthy',
|
|
'text-yellow-600': metric.status === 'warning',
|
|
'text-red-600': metric.status === 'error'
|
|
}">
|
|
{{ metric.status }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Storage Configuration -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<!-- Primary Storage -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-primary" name="ic:outline-database"></Icon>
|
|
<h3 class="text-lg font-semibold text-primary">Primary Storage Configuration</h3>
|
|
</div>
|
|
<rs-button variant="outline" size="sm" @click="showStorageModal = true">
|
|
<Icon class="mr-1" name="ic:outline-settings"></Icon>
|
|
Configure
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<!-- Storage Type -->
|
|
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<p class="font-medium">Storage Type</p>
|
|
<p class="text-sm text-gray-600">{{ storageConfig.type }}</p>
|
|
</div>
|
|
<div class="text-right">
|
|
<span
|
|
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full"
|
|
:class="{
|
|
'bg-green-100 text-green-800': storageConfig.status === 'connected',
|
|
'bg-red-100 text-red-800': storageConfig.status === 'disconnected',
|
|
'bg-yellow-100 text-yellow-800': storageConfig.status === 'reconnecting'
|
|
}"
|
|
>
|
|
{{ storageConfig.status }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Connection Details -->
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div class="text-center p-3 bg-blue-50 rounded-lg">
|
|
<p class="text-sm text-gray-600">Connection Pool</p>
|
|
<p class="font-bold text-blue-600">{{ storageConfig.connectionPool }}/{{ storageConfig.maxConnections }}</p>
|
|
</div>
|
|
<div class="text-center p-3 bg-green-50 rounded-lg">
|
|
<p class="text-sm text-gray-600">Response Time</p>
|
|
<p class="font-bold text-green-600">{{ storageConfig.responseTime }}ms</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Storage Metrics -->
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-gray-600">Used Space</span>
|
|
<span class="text-sm font-medium">{{ storageConfig.usedSpace }} / {{ storageConfig.totalSpace }}</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 rounded-full h-2">
|
|
<div
|
|
class="bg-blue-600 h-2 rounded-full"
|
|
:style="{ width: storageConfig.usagePercentage + '%' }"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Last Backup -->
|
|
<div class="flex items-center justify-between p-3 bg-green-50 rounded-lg">
|
|
<div>
|
|
<p class="font-medium text-green-800">Last Backup</p>
|
|
<p class="text-sm text-green-600">{{ storageConfig.lastBackup }}</p>
|
|
</div>
|
|
<Icon class="text-green-600" name="ic:outline-backup"></Icon>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Backup & Recovery -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-primary" name="ic:outline-backup"></Icon>
|
|
<h3 class="text-lg font-semibold text-primary">Backup & Recovery</h3>
|
|
</div>
|
|
<rs-button variant="outline" size="sm" @click="createBackup">
|
|
<Icon class="mr-1" name="ic:outline-backup"></Icon>
|
|
Create Backup
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<!-- Backup Schedule -->
|
|
<div class="p-3 bg-gray-50 rounded-lg">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<p class="font-medium">Automatic Backups</p>
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
v-model="backupConfig.autoBackupEnabled"
|
|
class="mr-2"
|
|
@change="updateBackupConfig"
|
|
>
|
|
<span class="text-sm">{{ backupConfig.autoBackupEnabled ? 'Enabled' : 'Disabled' }}</span>
|
|
</div>
|
|
</div>
|
|
<p class="text-sm text-gray-600">
|
|
Frequency: {{ backupConfig.frequency }} |
|
|
Retention: {{ backupConfig.retention }} days
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Recent Backups -->
|
|
<div>
|
|
<h4 class="font-medium text-gray-700 mb-2">Recent Backups</h4>
|
|
<div class="space-y-2">
|
|
<div
|
|
v-for="(backup, index) in recentBackups"
|
|
:key="index"
|
|
class="flex items-center justify-between p-2 bg-gray-50 rounded"
|
|
>
|
|
<div class="flex items-center">
|
|
<Icon
|
|
class="mr-2 text-sm"
|
|
:class="{
|
|
'text-green-500': backup.status === 'completed',
|
|
'text-yellow-500': backup.status === 'in-progress',
|
|
'text-red-500': backup.status === 'failed'
|
|
}"
|
|
:name="backup.status === 'completed' ? 'ic:outline-check-circle' :
|
|
backup.status === 'in-progress' ? 'ic:outline-hourglass-empty' :
|
|
'ic:outline-error'"
|
|
></Icon>
|
|
<div>
|
|
<p class="text-sm font-medium">{{ backup.name }}</p>
|
|
<p class="text-xs text-gray-500">{{ backup.size }} • {{ backup.timestamp }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-1">
|
|
<rs-button
|
|
variant="outline"
|
|
size="xs"
|
|
@click="downloadBackup(backup)"
|
|
:disabled="backup.status !== 'completed'"
|
|
>
|
|
<Icon class="text-xs" name="ic:outline-download"></Icon>
|
|
</rs-button>
|
|
<rs-button
|
|
variant="outline"
|
|
size="xs"
|
|
@click="restoreBackup(backup)"
|
|
:disabled="backup.status !== 'completed'"
|
|
>
|
|
<Icon class="text-xs" name="ic:outline-restore"></Icon>
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recovery Test -->
|
|
<div class="p-3 bg-yellow-50 rounded-lg">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="font-medium text-yellow-800">Recovery Test</p>
|
|
<p class="text-sm text-yellow-600">Last test: {{ recoveryTest.lastTest }}</p>
|
|
</div>
|
|
<rs-button
|
|
variant="outline"
|
|
size="sm"
|
|
@click="runRecoveryTest"
|
|
>
|
|
Run Test
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Queue Recovery Status -->
|
|
<rs-card class="mb-6">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-primary" name="ic:outline-restore"></Icon>
|
|
<h3 class="text-lg font-semibold text-primary">Queue Recovery Status</h3>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-sm text-gray-600">Last System Restart: {{ lastSystemRestart }}</span>
|
|
<rs-button variant="outline" size="sm" @click="showRecoveryDetails = true">
|
|
<Icon class="mr-1" name="ic:outline-info"></Icon>
|
|
View Details
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<!-- Recovery Statistics -->
|
|
<div class="space-y-4">
|
|
<h4 class="font-medium text-gray-700">Recovery Statistics</h4>
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-gray-600">Jobs Recovered</span>
|
|
<span class="font-medium text-green-600">{{ recoveryStats.jobsRecovered.toLocaleString() }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-gray-600">Jobs Lost</span>
|
|
<span class="font-medium text-red-600">{{ recoveryStats.jobsLost }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-gray-600">Recovery Time</span>
|
|
<span class="font-medium">{{ recoveryStats.recoveryTime }}</span>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<span class="text-sm text-gray-600">Success Rate</span>
|
|
<span class="font-medium text-blue-600">{{ recoveryStats.successRate }}%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recovery Timeline -->
|
|
<div class="space-y-4">
|
|
<h4 class="font-medium text-gray-700">Recovery Timeline</h4>
|
|
<div class="space-y-3">
|
|
<div
|
|
v-for="(event, index) in recoveryTimeline"
|
|
:key="index"
|
|
class="flex items-start"
|
|
>
|
|
<div
|
|
class="w-3 h-3 rounded-full mt-1 mr-3"
|
|
:class="{
|
|
'bg-green-500': event.status === 'completed',
|
|
'bg-yellow-500': event.status === 'in-progress',
|
|
'bg-red-500': event.status === 'failed'
|
|
}"
|
|
></div>
|
|
<div>
|
|
<p class="text-sm font-medium">{{ event.action }}</p>
|
|
<p class="text-xs text-gray-500">{{ event.timestamp }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Queue State -->
|
|
<div class="space-y-4">
|
|
<h4 class="font-medium text-gray-700">Current Queue State</h4>
|
|
<div class="space-y-3">
|
|
<div
|
|
v-for="(queue, index) in queueStates"
|
|
:key="index"
|
|
class="p-3 bg-gray-50 rounded-lg"
|
|
>
|
|
<div class="flex items-center justify-between mb-1">
|
|
<span class="text-sm font-medium">{{ queue.name }}</span>
|
|
<span
|
|
class="text-xs px-2 py-1 rounded-full"
|
|
:class="{
|
|
'bg-green-100 text-green-800': queue.status === 'healthy',
|
|
'bg-yellow-100 text-yellow-800': queue.status === 'recovering',
|
|
'bg-red-100 text-red-800': queue.status === 'error'
|
|
}"
|
|
>
|
|
{{ queue.status }}
|
|
</span>
|
|
</div>
|
|
<div class="flex justify-between text-xs text-gray-600">
|
|
<span>{{ queue.count }} jobs</span>
|
|
<span>{{ queue.lastProcessed }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Persistence Configuration -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-primary" name="ic:outline-settings"></Icon>
|
|
<h3 class="text-lg font-semibold text-primary">Persistence Settings</h3>
|
|
</div>
|
|
<rs-button @click="savePersistenceConfig">
|
|
<Icon class="mr-1" name="ic:outline-save"></Icon>
|
|
Save Configuration
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- General Settings -->
|
|
<div class="space-y-4">
|
|
<h4 class="font-medium text-gray-700">General Settings</h4>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Persistence Mode</label>
|
|
<select v-model="persistenceConfig.mode" class="w-full p-2 border border-gray-300 rounded-md">
|
|
<option value="immediate">Immediate (Every job)</option>
|
|
<option value="batch">Batch (Every N jobs)</option>
|
|
<option value="interval">Interval (Every N seconds)</option>
|
|
<option value="hybrid">Hybrid (Immediate + Batch)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div v-if="persistenceConfig.mode === 'batch'">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Batch Size</label>
|
|
<input
|
|
type="number"
|
|
v-model="persistenceConfig.batchSize"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="1"
|
|
>
|
|
</div>
|
|
|
|
<div v-if="persistenceConfig.mode === 'interval'">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Interval (seconds)</label>
|
|
<input
|
|
type="number"
|
|
v-model="persistenceConfig.interval"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="1"
|
|
>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Data Retention (days)</label>
|
|
<input
|
|
type="number"
|
|
v-model="persistenceConfig.retentionDays"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="1"
|
|
>
|
|
<p class="text-xs text-gray-500 mt-1">How long to keep completed job data</p>
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
v-model="persistenceConfig.compressData"
|
|
class="mr-2"
|
|
>
|
|
<span class="text-sm text-gray-700">Enable data compression</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recovery Settings -->
|
|
<div class="space-y-4">
|
|
<h4 class="font-medium text-gray-700">Recovery Settings</h4>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Recovery Strategy</label>
|
|
<select v-model="persistenceConfig.recoveryStrategy" class="w-full p-2 border border-gray-300 rounded-md">
|
|
<option value="full">Full Recovery (All jobs)</option>
|
|
<option value="priority">Priority Recovery (High priority first)</option>
|
|
<option value="recent">Recent Recovery (Last N hours)</option>
|
|
<option value="selective">Selective Recovery (Manual selection)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div v-if="persistenceConfig.recoveryStrategy === 'recent'">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Recovery Window (hours)</label>
|
|
<input
|
|
type="number"
|
|
v-model="persistenceConfig.recoveryWindow"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="1"
|
|
>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Max Recovery Time (seconds)</label>
|
|
<input
|
|
type="number"
|
|
v-model="persistenceConfig.maxRecoveryTime"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="1"
|
|
>
|
|
<p class="text-xs text-gray-500 mt-1">Maximum time allowed for recovery process</p>
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
v-model="persistenceConfig.autoRecovery"
|
|
class="mr-2"
|
|
>
|
|
<span class="text-sm text-gray-700">Enable automatic recovery on startup</span>
|
|
</div>
|
|
|
|
<div class="flex items-center">
|
|
<input
|
|
type="checkbox"
|
|
v-model="persistenceConfig.validateRecovery"
|
|
class="mr-2"
|
|
>
|
|
<span class="text-sm text-gray-700">Validate recovered jobs before processing</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Storage Configuration Modal -->
|
|
<rs-modal v-model="showStorageModal" title="Storage Configuration">
|
|
<div class="space-y-6">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Storage Type</label>
|
|
<select v-model="storageConfig.type" class="w-full p-2 border border-gray-300 rounded-md">
|
|
<option value="Redis">Redis</option>
|
|
<option value="PostgreSQL">PostgreSQL</option>
|
|
<option value="MongoDB">MongoDB</option>
|
|
<option value="MySQL">MySQL</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Connection String</label>
|
|
<input
|
|
type="text"
|
|
v-model="storageConfig.connectionString"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
placeholder="redis://localhost:6379"
|
|
>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Max Connections</label>
|
|
<input
|
|
type="number"
|
|
v-model="storageConfig.maxConnections"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="1"
|
|
>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Connection Timeout (ms)</label>
|
|
<input
|
|
type="number"
|
|
v-model="storageConfig.connectionTimeout"
|
|
class="w-full p-2 border border-gray-300 rounded-md"
|
|
min="100"
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<div class="flex justify-end gap-3">
|
|
<rs-button variant="outline" @click="showStorageModal = false">
|
|
Cancel
|
|
</rs-button>
|
|
<rs-button @click="saveStorageConfig">
|
|
Save Configuration
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
</rs-modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
definePageMeta({
|
|
title: "Queue Persistence",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{
|
|
name: "Notification",
|
|
path: "/notification",
|
|
},
|
|
{
|
|
name: "Queue & Scheduler",
|
|
path: "/notification/queue-scheduler",
|
|
},
|
|
{
|
|
name: "Persistence",
|
|
path: "/notification/queue-scheduler/persistence",
|
|
},
|
|
],
|
|
});
|
|
|
|
// Reactive data
|
|
const showStorageModal = ref(false);
|
|
const showRecoveryDetails = ref(false);
|
|
|
|
// Persistence metrics
|
|
const persistenceMetrics = ref([
|
|
{
|
|
title: "Storage Health",
|
|
value: "Healthy",
|
|
icon: "ic:outline-health-and-safety",
|
|
bgColor: "bg-green-100",
|
|
iconColor: "text-green-600",
|
|
valueColor: "text-green-600",
|
|
status: "healthy"
|
|
},
|
|
{
|
|
title: "Persisted Jobs",
|
|
value: "847,293",
|
|
icon: "ic:outline-storage",
|
|
bgColor: "bg-blue-100",
|
|
iconColor: "text-blue-600",
|
|
valueColor: "text-blue-600"
|
|
},
|
|
{
|
|
title: "Recovery Rate",
|
|
value: "99.97%",
|
|
icon: "ic:outline-restore",
|
|
bgColor: "bg-purple-100",
|
|
iconColor: "text-purple-600",
|
|
valueColor: "text-purple-600",
|
|
status: "healthy"
|
|
},
|
|
{
|
|
title: "Storage Usage",
|
|
value: "67%",
|
|
icon: "ic:outline-pie-chart",
|
|
bgColor: "bg-yellow-100",
|
|
iconColor: "text-yellow-600",
|
|
valueColor: "text-yellow-600",
|
|
status: "warning"
|
|
}
|
|
]);
|
|
|
|
// Storage configuration
|
|
const storageConfig = ref({
|
|
type: "Redis",
|
|
status: "connected",
|
|
connectionPool: 8,
|
|
maxConnections: 20,
|
|
responseTime: 2.3,
|
|
usedSpace: "2.4 GB",
|
|
totalSpace: "10 GB",
|
|
usagePercentage: 67,
|
|
lastBackup: "2 hours ago",
|
|
connectionString: "redis://localhost:6379",
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
// Backup configuration
|
|
const backupConfig = ref({
|
|
autoBackupEnabled: true,
|
|
frequency: "Every 6 hours",
|
|
retention: 30
|
|
});
|
|
|
|
// Recent backups
|
|
const recentBackups = ref([
|
|
{
|
|
name: "queue-backup-2024-01-15-14-30",
|
|
size: "1.2 GB",
|
|
timestamp: "2 hours ago",
|
|
status: "completed"
|
|
},
|
|
{
|
|
name: "queue-backup-2024-01-15-08-30",
|
|
size: "1.1 GB",
|
|
timestamp: "8 hours ago",
|
|
status: "completed"
|
|
},
|
|
{
|
|
name: "queue-backup-2024-01-15-02-30",
|
|
size: "1.0 GB",
|
|
timestamp: "14 hours ago",
|
|
status: "completed"
|
|
},
|
|
{
|
|
name: "queue-backup-2024-01-14-20-30",
|
|
size: "987 MB",
|
|
timestamp: "20 hours ago",
|
|
status: "completed"
|
|
}
|
|
]);
|
|
|
|
// Recovery test
|
|
const recoveryTest = ref({
|
|
lastTest: "3 days ago",
|
|
status: "passed"
|
|
});
|
|
|
|
// System restart info
|
|
const lastSystemRestart = ref("5 days ago");
|
|
|
|
// Recovery statistics
|
|
const recoveryStats = ref({
|
|
jobsRecovered: 15847,
|
|
jobsLost: 3,
|
|
recoveryTime: "2.3 seconds",
|
|
successRate: 99.97
|
|
});
|
|
|
|
// Recovery timeline
|
|
const recoveryTimeline = ref([
|
|
{
|
|
action: "System startup detected",
|
|
timestamp: "5 days ago, 09:15:23",
|
|
status: "completed"
|
|
},
|
|
{
|
|
action: "Storage connection established",
|
|
timestamp: "5 days ago, 09:15:24",
|
|
status: "completed"
|
|
},
|
|
{
|
|
action: "Queue data recovery initiated",
|
|
timestamp: "5 days ago, 09:15:25",
|
|
status: "completed"
|
|
},
|
|
{
|
|
action: "15,847 jobs recovered successfully",
|
|
timestamp: "5 days ago, 09:15:27",
|
|
status: "completed"
|
|
},
|
|
{
|
|
action: "Queue processing resumed",
|
|
timestamp: "5 days ago, 09:15:28",
|
|
status: "completed"
|
|
}
|
|
]);
|
|
|
|
// Queue states
|
|
const queueStates = ref([
|
|
{
|
|
name: "High Priority",
|
|
count: 234,
|
|
status: "healthy",
|
|
lastProcessed: "2 seconds ago"
|
|
},
|
|
{
|
|
name: "Medium Priority",
|
|
count: 1847,
|
|
status: "healthy",
|
|
lastProcessed: "1 second ago"
|
|
},
|
|
{
|
|
name: "Low Priority",
|
|
count: 3421,
|
|
status: "healthy",
|
|
lastProcessed: "5 seconds ago"
|
|
},
|
|
{
|
|
name: "Bulk Operations",
|
|
count: 2502,
|
|
status: "recovering",
|
|
lastProcessed: "30 seconds ago"
|
|
}
|
|
]);
|
|
|
|
// Persistence configuration
|
|
const persistenceConfig = ref({
|
|
mode: "hybrid",
|
|
batchSize: 100,
|
|
interval: 30,
|
|
retentionDays: 30,
|
|
compressData: true,
|
|
recoveryStrategy: "priority",
|
|
recoveryWindow: 24,
|
|
maxRecoveryTime: 300,
|
|
autoRecovery: true,
|
|
validateRecovery: true
|
|
});
|
|
|
|
// Methods
|
|
const testPersistence = () => {
|
|
console.log('Running persistence test...');
|
|
// Simulate persistence test
|
|
};
|
|
|
|
const createBackup = () => {
|
|
console.log('Creating backup...');
|
|
// Add new backup to the list
|
|
const now = new Date();
|
|
const timestamp = now.toISOString().slice(0, 19).replace(/:/g, '-');
|
|
recentBackups.value.unshift({
|
|
name: `queue-backup-${timestamp}`,
|
|
size: "1.3 GB",
|
|
timestamp: "Just now",
|
|
status: "in-progress"
|
|
});
|
|
|
|
// Simulate completion after 3 seconds
|
|
setTimeout(() => {
|
|
recentBackups.value[0].status = "completed";
|
|
}, 3000);
|
|
};
|
|
|
|
const downloadBackup = (backup) => {
|
|
console.log('Downloading backup:', backup.name);
|
|
// Simulate download
|
|
};
|
|
|
|
const restoreBackup = (backup) => {
|
|
console.log('Restoring backup:', backup.name);
|
|
// Simulate restore
|
|
};
|
|
|
|
const runRecoveryTest = () => {
|
|
console.log('Running recovery test...');
|
|
recoveryTest.value.lastTest = "Just now";
|
|
// Simulate test
|
|
};
|
|
|
|
const updateBackupConfig = () => {
|
|
console.log('Updating backup configuration:', backupConfig.value);
|
|
// Save backup config
|
|
};
|
|
|
|
const savePersistenceConfig = () => {
|
|
console.log('Saving persistence configuration:', persistenceConfig.value);
|
|
// Save persistence config
|
|
};
|
|
|
|
const saveStorageConfig = () => {
|
|
console.log('Saving storage configuration:', storageConfig.value);
|
|
showStorageModal.value = false;
|
|
// Save storage config
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped></style> |