feat: Waves 3+4 — frontend wiring, NATS integration, stores (19 files)
All checks were successful
Test Asgard Runner / test (push) Successful in 2s

Frontend:
- Wire Dashboard quick actions (start/stop/trigger wipe) + next wipe schedule
- Wire Console WebSocket streaming for real-time output
- Implement TOTP 2FA challenge flow in LoginView
- Wire Plugin load/unload toggle + uninstall buttons with confirmations
- Wire WipesView profile selector, disable trigger when no profiles
- Build full WipeProfiles create/edit modal with all config fields
- Wire MapsView file upload with multipart FormData
- Fix SettingsView empty catch blocks → toast error messages
- Fix stale localStorage token reads in CSV exports → auth store
- Fix auth store hardcoded permissions → JWT-decoded role permissions
- Fix wipe store onMounted lifecycle bug → explicit subscribe action
- Update EarlyAccessView from countdown to "Now Live" state

Backend:
- Wire wipe trigger to publish NATS cmd (corrosion.{id}.cmd.wipe)
- Wire plugin reload/uninstall to publish NATS cmd
- Expand NatsBridgeService: add files, wipe status, server status subs
- Add PATCH schedules/:id/toggle endpoint for task toggling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-21 13:34:09 -05:00
parent a181ed7ded
commit 8bb6cc0890
19 changed files with 776 additions and 139 deletions

View File

@@ -1,13 +1,38 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useServerStore } from '@/stores/server'
import { useWipeStore } from '@/stores/wipe'
const router = useRouter()
const auth = useAuthStore()
const server = useServerStore()
const wipe = useWipeStore()
onMounted(() => {
onMounted(async () => {
server.fetchServer()
try {
await wipe.fetchSchedules()
} catch {
// Non-critical — dashboard still loads without wipe data
}
})
const nextWipeDate = computed<string>(() => {
const upcoming = wipe.schedules
.filter(s => s.is_active && s.next_scheduled_run)
.map(s => new Date(s.next_scheduled_run!))
.sort((a, b) => a.getTime() - b.getTime())
if (upcoming.length === 0) return 'Not Scheduled'
return upcoming[0].toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
})
function statusColor(status: string | undefined): string {
@@ -67,7 +92,7 @@ function formatUptime(seconds: number | undefined): string {
<!-- Next Wipe -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<p class="text-sm text-neutral-400 mb-2">Next Wipe</p>
<p class="text-2xl font-bold text-neutral-100">Not Scheduled</p>
<p class="text-2xl font-bold text-neutral-100">{{ nextWipeDate }}</p>
</div>
<!-- Uptime -->
@@ -83,17 +108,20 @@ function formatUptime(seconds: number | undefined): string {
<div class="flex flex-wrap gap-3">
<button
:disabled="server.connection?.connection_status === 'connected'"
@click="server.startServer()"
class="px-4 py-2.5 bg-green-600/20 hover:bg-green-600/30 disabled:opacity-30 disabled:cursor-not-allowed text-green-400 border border-green-600/30 rounded-lg text-sm font-medium transition-colors"
>
Start Server
</button>
<button
:disabled="server.connection?.connection_status !== 'connected'"
@click="server.stopServer()"
class="px-4 py-2.5 bg-red-600/20 hover:bg-red-600/30 disabled:opacity-30 disabled:cursor-not-allowed text-red-400 border border-red-600/30 rounded-lg text-sm font-medium transition-colors"
>
Stop Server
</button>
<button
@click="router.push('/wipes')"
class="px-4 py-2.5 bg-neutral-800 hover:bg-neutral-700 text-neutral-300 rounded-lg text-sm font-medium transition-colors"
>
Trigger Wipe