feat: Waves 3+4 — frontend wiring, NATS integration, stores (19 files)
All checks were successful
Test Asgard Runner / test (push) Successful in 2s
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:
@@ -1,26 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { Shield, Users, Star, MessageCircle, Clock, ChevronRight, Check, Zap, Terminal, RefreshCw, LayoutDashboard } from 'lucide-vue-next'
|
||||
|
||||
// ---------- Countdown ----------
|
||||
const targetDate = new Date('2026-02-28T12:00:00-05:00')
|
||||
const now = ref(Date.now())
|
||||
let timer: ReturnType<typeof setInterval>
|
||||
|
||||
const countdown = computed(() => {
|
||||
const diff = Math.max(0, targetDate.getTime() - now.value)
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
|
||||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
|
||||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
|
||||
const seconds = Math.floor((diff % (1000 * 60)) / 1000)
|
||||
return { days, hours, minutes, seconds }
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
timer = setInterval(() => { now.value = Date.now() }, 1000)
|
||||
})
|
||||
onUnmounted(() => clearInterval(timer))
|
||||
|
||||
// ---------- Email capture ----------
|
||||
const email = ref('')
|
||||
const serverCount = ref('')
|
||||
@@ -92,19 +73,19 @@ const totalVotes = computed(() => voteItems.value.reduce((sum, i) => sum + i.vot
|
||||
<!-- Hero -->
|
||||
<section class="relative overflow-hidden">
|
||||
<div class="max-w-4xl mx-auto px-6 pt-20 pb-16 text-center">
|
||||
<span class="inline-block px-4 py-1.5 bg-oxide-500/10 border border-oxide-500/20 rounded-full text-oxide-400 text-sm font-medium mb-6">
|
||||
Early Access Opening Soon
|
||||
<span class="inline-block px-4 py-1.5 bg-green-500/10 border border-green-500/20 rounded-full text-green-400 text-sm font-medium mb-6">
|
||||
Early Access Is Now Open
|
||||
</span>
|
||||
<h1 class="text-4xl md:text-5xl font-bold text-neutral-100 mb-4 tracking-tight">
|
||||
Wipe Night Is About to<br />
|
||||
<span class="text-oxide-500">Get Easier.</span>
|
||||
Wipe Night Just Got<br />
|
||||
<span class="text-oxide-500">A Lot Easier.</span>
|
||||
</h1>
|
||||
<p class="text-lg text-neutral-400 max-w-xl mx-auto mb-10">
|
||||
Corrosion is entering limited early access. Install once. Automate everything. Never SSH again.
|
||||
Corrosion is live in limited early access. Install once. Automate everything. Never SSH again.
|
||||
</p>
|
||||
<div class="flex items-center justify-center gap-4">
|
||||
<a href="#join" class="px-8 py-3.5 bg-oxide-600 hover:bg-oxide-700 text-white font-semibold rounded-lg transition-colors">
|
||||
Join Early Access
|
||||
Claim Your Spot
|
||||
</a>
|
||||
<a href="#demo" class="px-8 py-3.5 bg-neutral-800 hover:bg-neutral-700 text-neutral-200 font-semibold rounded-lg border border-neutral-700 transition-colors">
|
||||
View Demo Architecture
|
||||
@@ -114,39 +95,16 @@ const totalVotes = computed(() => voteItems.value.reduce((sum, i) => sum + i.vot
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[400px] bg-oxide-500/8 rounded-full blur-3xl pointer-events-none" />
|
||||
</section>
|
||||
|
||||
<!-- Countdown -->
|
||||
<!-- Early Access Live Banner -->
|
||||
<section class="py-12 border-t border-neutral-800">
|
||||
<div class="max-w-3xl mx-auto px-6 text-center">
|
||||
<p class="text-sm text-neutral-500 uppercase tracking-wider mb-6">Early Access Opens In</p>
|
||||
<div class="flex items-center justify-center gap-4 md:gap-6">
|
||||
<div class="text-center">
|
||||
<div class="w-20 h-20 bg-neutral-900 border border-neutral-800 rounded-xl flex items-center justify-center">
|
||||
<span class="text-3xl font-bold text-oxide-400 tabular-nums">{{ countdown.days }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-neutral-500 mt-2">Days</p>
|
||||
</div>
|
||||
<span class="text-2xl text-neutral-700 font-light">:</span>
|
||||
<div class="text-center">
|
||||
<div class="w-20 h-20 bg-neutral-900 border border-neutral-800 rounded-xl flex items-center justify-center">
|
||||
<span class="text-3xl font-bold text-oxide-400 tabular-nums">{{ String(countdown.hours).padStart(2, '0') }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-neutral-500 mt-2">Hours</p>
|
||||
</div>
|
||||
<span class="text-2xl text-neutral-700 font-light">:</span>
|
||||
<div class="text-center">
|
||||
<div class="w-20 h-20 bg-neutral-900 border border-neutral-800 rounded-xl flex items-center justify-center">
|
||||
<span class="text-3xl font-bold text-neutral-200 tabular-nums">{{ String(countdown.minutes).padStart(2, '0') }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-neutral-500 mt-2">Minutes</p>
|
||||
</div>
|
||||
<span class="text-2xl text-neutral-700 font-light">:</span>
|
||||
<div class="text-center">
|
||||
<div class="w-20 h-20 bg-neutral-900 border border-neutral-800 rounded-xl flex items-center justify-center">
|
||||
<span class="text-3xl font-bold text-neutral-200 tabular-nums">{{ String(countdown.seconds).padStart(2, '0') }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-neutral-500 mt-2">Seconds</p>
|
||||
</div>
|
||||
<div class="inline-flex items-center gap-3 px-6 py-4 bg-green-500/10 border border-green-500/20 rounded-2xl">
|
||||
<div class="w-2.5 h-2.5 bg-green-400 rounded-full animate-pulse shrink-0" />
|
||||
<p class="text-green-300 font-semibold text-lg">Early Access is now live — founding admin spots are limited.</p>
|
||||
</div>
|
||||
<p class="text-neutral-500 text-sm mt-4">
|
||||
Sign up below to lock in founding pricing before spots run out.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -182,13 +140,13 @@ const totalVotes = computed(() => voteItems.value.reduce((sum, i) => sum + i.vot
|
||||
<!-- Email Capture -->
|
||||
<section id="join" class="py-16 border-t border-neutral-800">
|
||||
<div class="max-w-md mx-auto px-6">
|
||||
<h2 class="text-2xl font-bold text-neutral-100 mb-2 text-center">Get on the List</h2>
|
||||
<p class="text-neutral-400 text-center mb-8">Be first to know when early access opens.</p>
|
||||
<h2 class="text-2xl font-bold text-neutral-100 mb-2 text-center">Claim Your Founding Spot</h2>
|
||||
<p class="text-neutral-400 text-center mb-8">Early access is open now. Spots are limited — lock in founding pricing today.</p>
|
||||
|
||||
<div v-if="submitted" class="bg-green-500/10 border border-green-500/20 rounded-xl p-8 text-center">
|
||||
<Check class="w-10 h-10 text-green-400 mx-auto mb-3" />
|
||||
<h3 class="text-lg font-semibold text-neutral-100 mb-1">You're on the list.</h3>
|
||||
<p class="text-sm text-neutral-400">We'll reach out when early access opens.</p>
|
||||
<h3 class="text-lg font-semibold text-neutral-100 mb-1">You're in.</h3>
|
||||
<p class="text-sm text-neutral-400">We'll be in touch shortly with your access details.</p>
|
||||
</div>
|
||||
|
||||
<form v-else @submit.prevent="handleSubmit" class="space-y-4">
|
||||
@@ -341,12 +299,12 @@ const totalVotes = computed(() => voteItems.value.reduce((sum, i) => sum + i.vot
|
||||
</div>
|
||||
<div class="ml-4 w-px h-4 bg-neutral-800" />
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-8 h-8 bg-oxide-500/10 border border-oxide-500/20 rounded-full flex items-center justify-center shrink-0 mt-0.5">
|
||||
<ChevronRight class="w-4 h-4 text-oxide-400" />
|
||||
<div class="w-8 h-8 bg-green-500/10 border border-green-500/20 rounded-full flex items-center justify-center shrink-0 mt-0.5">
|
||||
<Check class="w-4 h-4 text-green-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-oxide-400">Week 2 — Early Access Opens</p>
|
||||
<p class="text-xs text-neutral-500 mt-0.5">Founding Admin licenses go live.</p>
|
||||
<p class="text-sm font-medium text-neutral-200">Week 2 — Early Access Open</p>
|
||||
<p class="text-xs text-neutral-500 mt-0.5">Founding Admin licenses are live — claim yours now.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 w-px h-4 bg-neutral-800" />
|
||||
|
||||
Reference in New Issue
Block a user