diff --git a/admin.go b/admin.go index 0ae1ff4..61f03c4 100644 --- a/admin.go +++ b/admin.go @@ -3,6 +3,7 @@ package main import ( "database/sql" "encoding/json" + "log" "net/http" "strconv" "strings" @@ -60,6 +61,69 @@ func handleAdminUpdateRepeater(db *sql.DB) http.HandlerFunc { } } +func handleBackfillBM(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed) + return + } + + bmSet := loadBMRepeaters("") + if len(bmSet) == 0 { + http.Error(w, `{"error":"could not load BM repeaters"}`, http.StatusInternalServerError) + return + } + log.Printf("BM backfill: downloaded %d BM callsigns", len(bmSet)) + + rows, err := db.Query("SELECT id, callsign FROM repeaters WHERE NOT networks @> ARRAY['Brandmeister']") + if err != nil { + http.Error(w, `{"error":"database query failed"}`, http.StatusInternalServerError) + return + } + defer rows.Close() + + var ids []int + for rows.Next() { + var id int + var callsign string + if err := rows.Scan(&id, &callsign); err != nil { + continue + } + if bmSet[strings.ToUpper(strings.TrimSpace(callsign))] { + ids = append(ids, id) + } + } + + if len(ids) == 0 { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{"updated": 0}) + return + } + + tx, err := db.Begin() + if err != nil { + http.Error(w, `{"error":"transaction failed"}`, http.StatusInternalServerError) + return + } + + updated := 0 + for _, id := range ids { + if _, err := tx.Exec("UPDATE repeaters SET networks = array_append(networks, 'Brandmeister') WHERE id = $1", id); err == nil { + updated++ + } + } + + if err := tx.Commit(); err != nil { + http.Error(w, `{"error":"commit failed"}`, http.StatusInternalServerError) + return + } + + log.Printf("BM backfill: updated %d repeaters", updated) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{"updated": updated}) + } +} + func handleAdminRepeaters(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { diff --git a/main.go b/main.go index 4c12b54..24eb84e 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,7 @@ func main() { adminAPI := http.NewServeMux() adminAPI.HandleFunc("/admin/api/repeaters", handleAdminRepeaters(db)) adminAPI.HandleFunc("/admin/api/repeaters/update", handleAdminUpdateRepeater(db)) + adminAPI.HandleFunc("/admin/api/repeaters/backfill-bm", handleBackfillBM(db)) mux.Handle("/admin/api/", adminAuth(adminToken, adminAPI)) log.Println("Admin interface enabled at /admin/") } diff --git a/static/admin.html b/static/admin.html index 588eac0..d401c35 100644 --- a/static/admin.html +++ b/static/admin.html @@ -171,6 +171,24 @@ .logout-btn:hover { background: var(--hover-bg); } + .topbar-btn { + background: none; + border: 1px solid var(--border); + border-radius: 6px; + padding: 6px 12px; + font-size: 12px; + color: var(--text-muted); + cursor: pointer; + white-space: nowrap; + } + + .topbar-btn:hover { background: var(--hover-bg); } + + .topbar-btn:disabled { + opacity: 0.5; + cursor: default; + } + .table-wrap { overflow-x: auto; padding: 16px 24px; @@ -456,6 +474,7 @@