feat: implement update functionality for targets and remove legacy HelloWorld component
Build and Push / build (nucleus, amd64, linux) (push) Successful in 16s

This commit is contained in:
2026-07-17 09:12:10 +01:00
parent fffa7d94bd
commit ca2d6d05d4
3 changed files with 83 additions and 106 deletions
-95
View File
@@ -1,95 +0,0 @@
<script setup>
import { ref } from 'vue'
import viteLogo from '../assets/vite.svg'
import heroImg from '../assets/hero.png'
import vueLogo from '../assets/vue.svg'
const count = ref(0)
</script>
<template>
<section id="center">
<div class="hero">
<img :src="heroImg" class="base" width="170" height="179" alt="" />
<img :src="vueLogo" class="framework" alt="Vue logo" />
<img :src="viteLogo" class="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>Edit <code>src/App.vue</code> and save to test <code>HMR</code></p>
</div>
<button type="button" class="counter" @click="count++">
Count is {{ count }}
</button>
</section>
<div class="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg class="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img class="logo" :src="viteLogo" alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://vuejs.org/" target="_blank">
<img class="button-icon" :src="vueLogo" alt="" />
Learn more
</a>
</li>
</ul>
</div>
<div id="social">
<svg class="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg class="button-icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg class="button-icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg class="button-icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg class="button-icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div>
</section>
<div class="ticks"></div>
<section id="spacer"></section>
</template>
+55 -11
View File
@@ -26,7 +26,10 @@
</div> </div>
<div> <div>
<button type="submit" class="w-full bg-accent hover:bg-accent/80 text-bg font-bold py-2 px-4 rounded-lg shadow-md hover:shadow-lg transition-all transform hover:-translate-y-0.5 hover:cursor-pointer"> <button type="submit" class="w-full bg-accent hover:bg-accent/80 text-bg font-bold py-2 px-4 rounded-lg shadow-md hover:shadow-lg transition-all transform hover:-translate-y-0.5 hover:cursor-pointer">
Add Target {{ isEditing ? 'Update Target' : 'Add Target' }}
</button>
<button v-if="isEditing" type="button" @click="cancelEdit" class="w-full mt-2 bg-transparent border border-border-subtle text-body hover:text-heading font-medium py-1 px-4 rounded-lg transition-colors hover:cursor-pointer text-sm">
Cancel
</button> </button>
</div> </div>
</form> </form>
@@ -61,6 +64,7 @@
{{ target.last_scan_at ? new Date(target.last_scan_at).toLocaleString() : 'Never' }} {{ target.last_scan_at ? new Date(target.last_scan_at).toLocaleString() : 'Never' }}
</td> </td>
<td class="px-6 py-4 text-right space-x-3"> <td class="px-6 py-4 text-right space-x-3">
<button @click="editTarget(target)" class="text-blue-400 hover:text-blue-300 font-medium transition-colors hover:cursor-pointer">Edit</button>
<button @click="runScan(target.id)" class="text-accent hover:text-accent/80 font-medium transition-colors hover:cursor-pointer">Run Now</button> <button @click="runScan(target.id)" class="text-accent hover:text-accent/80 font-medium transition-colors hover:cursor-pointer">Run Now</button>
<button @click="deleteTarget(target.id)" class="text-red-400 hover:text-red-300 font-medium transition-colors hover:cursor-pointer">Delete</button> <button @click="deleteTarget(target.id)" class="text-red-400 hover:text-red-300 font-medium transition-colors hover:cursor-pointer">Delete</button>
</td> </td>
@@ -79,7 +83,8 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
const targets = ref([]) const targets = ref([])
const form = ref({ name: '', address: '', schedule: 'manual', customSchedule: '' }) const form = ref({ id: null, name: '', address: '', schedule: 'manual', customSchedule: '' })
const isEditing = ref(false)
const loadTargets = async () => { const loadTargets = async () => {
try { try {
@@ -101,19 +106,58 @@ const addTarget = async () => {
} }
} }
await fetch('/api/targets', { if (isEditing.value) {
method: 'POST', await fetch(`/api/targets/${form.value.id}`, {
headers: { 'Content-Type': 'application/json' }, method: 'PUT',
body: JSON.stringify({ headers: { 'Content-Type': 'application/json' },
name: form.value.name, body: JSON.stringify({
address: form.value.address, name: form.value.name,
schedule: finalSchedule address: form.value.address,
schedule: finalSchedule
})
}) })
}) } else {
form.value = { name: '', address: '', schedule: 'manual', customSchedule: '' } await fetch('/api/targets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: form.value.name,
address: form.value.address,
schedule: finalSchedule
})
})
}
cancelEdit()
loadTargets() loadTargets()
} }
const editTarget = (target) => {
isEditing.value = true
let sched = target.schedule
let custom = ''
if (['manual', '@midnight', '@hourly', '0 0 * * 0'].includes(sched)) {
// Standard schedule
} else {
custom = sched
sched = 'custom'
}
form.value = {
id: target.id,
name: target.name,
address: target.address,
schedule: sched,
customSchedule: custom
}
}
const cancelEdit = () => {
isEditing.value = false
form.value = { id: null, name: '', address: '', schedule: 'manual', customSchedule: '' }
}
const deleteTarget = async (id) => { const deleteTarget = async (id) => {
if (!confirm('Delete target?')) return if (!confirm('Delete target?')) return
await fetch(`/api/targets/${id}`, { method: 'DELETE' }) await fetch(`/api/targets/${id}`, { method: 'DELETE' })
+28
View File
@@ -18,6 +18,7 @@ func RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /api/targets", AuthMiddleware(getTargets)) mux.HandleFunc("GET /api/targets", AuthMiddleware(getTargets))
mux.HandleFunc("POST /api/targets", AuthMiddleware(createTarget)) mux.HandleFunc("POST /api/targets", AuthMiddleware(createTarget))
mux.HandleFunc("PUT /api/targets/{id}", AuthMiddleware(updateTarget))
mux.HandleFunc("DELETE /api/targets/{id}", AuthMiddleware(deleteTarget)) mux.HandleFunc("DELETE /api/targets/{id}", AuthMiddleware(deleteTarget))
mux.HandleFunc("POST /api/targets/{id}/scan", AuthMiddleware(triggerScan)) mux.HandleFunc("POST /api/targets/{id}/scan", AuthMiddleware(triggerScan))
mux.HandleFunc("GET /api/scans", AuthMiddleware(getScans)) mux.HandleFunc("GET /api/scans", AuthMiddleware(getScans))
@@ -65,6 +66,33 @@ func createTarget(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(t) json.NewEncoder(w).Encode(t)
} }
func updateTarget(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
var t models.Target
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
_, err = db.DB.Exec("UPDATE targets SET name = ?, address = ?, schedule = ? WHERE id = ?", t.Name, t.Address, t.Schedule, id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
scheduler.ReloadScheduler()
w.Header().Set("Content-Type", "application/json")
t.ID = id
json.NewEncoder(w).Encode(t)
}
func deleteTarget(w http.ResponseWriter, r *http.Request) { func deleteTarget(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id") idStr := r.PathValue("id")
id, err := strconv.Atoi(idStr) id, err := strconv.Atoi(idStr)