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>
|
||||||
<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' })
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user