feat: implement update functionality for targets and remove legacy HelloWorld component
Build and Push / build (nucleus, amd64, linux) (push) Successful in 16s
Build and Push / build (nucleus, amd64, linux) (push) Successful in 16s
This commit is contained in:
@@ -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>
|
||||
@@ -26,7 +26,10 @@
|
||||
</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">
|
||||
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>
|
||||
</div>
|
||||
</form>
|
||||
@@ -61,6 +64,7 @@
|
||||
{{ target.last_scan_at ? new Date(target.last_scan_at).toLocaleString() : 'Never' }}
|
||||
</td>
|
||||
<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="deleteTarget(target.id)" class="text-red-400 hover:text-red-300 font-medium transition-colors hover:cursor-pointer">Delete</button>
|
||||
</td>
|
||||
@@ -79,7 +83,8 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
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 () => {
|
||||
try {
|
||||
@@ -101,19 +106,58 @@ const addTarget = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
await fetch('/api/targets', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: form.value.name,
|
||||
address: form.value.address,
|
||||
schedule: finalSchedule
|
||||
if (isEditing.value) {
|
||||
await fetch(`/api/targets/${form.value.id}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: form.value.name,
|
||||
address: form.value.address,
|
||||
schedule: finalSchedule
|
||||
})
|
||||
})
|
||||
})
|
||||
form.value = { name: '', address: '', schedule: 'manual', customSchedule: '' }
|
||||
} else {
|
||||
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()
|
||||
}
|
||||
|
||||
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) => {
|
||||
if (!confirm('Delete target?')) return
|
||||
await fetch(`/api/targets/${id}`, { method: 'DELETE' })
|
||||
|
||||
@@ -18,6 +18,7 @@ func RegisterRoutes(mux *http.ServeMux) {
|
||||
|
||||
mux.HandleFunc("GET /api/targets", AuthMiddleware(getTargets))
|
||||
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("POST /api/targets/{id}/scan", AuthMiddleware(triggerScan))
|
||||
mux.HandleFunc("GET /api/scans", AuthMiddleware(getScans))
|
||||
@@ -65,6 +66,33 @@ func createTarget(w http.ResponseWriter, r *http.Request) {
|
||||
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) {
|
||||
idStr := r.PathValue("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
|
||||
Reference in New Issue
Block a user