parent
556858a28d
commit
80d7c07071
3 changed files with 0 additions and 104 deletions
64
admin.go
64
admin.go
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -61,69 +60,6 @@ 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 {
|
func handleAdminRepeaters(db *sql.DB) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodGet {
|
||||||
|
|
|
||||||
1
main.go
1
main.go
|
|
@ -44,7 +44,6 @@ func main() {
|
||||||
adminAPI := http.NewServeMux()
|
adminAPI := http.NewServeMux()
|
||||||
adminAPI.HandleFunc("/admin/api/repeaters", handleAdminRepeaters(db))
|
adminAPI.HandleFunc("/admin/api/repeaters", handleAdminRepeaters(db))
|
||||||
adminAPI.HandleFunc("/admin/api/repeaters/update", handleAdminUpdateRepeater(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))
|
mux.Handle("/admin/api/", adminAuth(adminToken, adminAPI))
|
||||||
log.Println("Admin interface enabled at /admin/")
|
log.Println("Admin interface enabled at /admin/")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,24 +171,6 @@
|
||||||
|
|
||||||
.logout-btn:hover { background: var(--hover-bg); }
|
.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 {
|
.table-wrap {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
|
|
@ -474,7 +456,6 @@
|
||||||
<h1>DMRmap Admin</h1>
|
<h1>DMRmap Admin</h1>
|
||||||
<input type="text" class="search-input" id="search-input" placeholder="Search callsign, city, country, network..." />
|
<input type="text" class="search-input" id="search-input" placeholder="Search callsign, city, country, network..." />
|
||||||
<span class="topbar-info" id="total-info"></span>
|
<span class="topbar-info" id="total-info"></span>
|
||||||
<button class="topbar-btn" id="backfill-bm-btn">Backfill BM</button>
|
|
||||||
<button class="logout-btn" id="logout-btn">Logout</button>
|
<button class="logout-btn" id="logout-btn">Logout</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
|
|
@ -923,26 +904,6 @@
|
||||||
showLogin();
|
showLogin();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Backfill BM
|
|
||||||
var backfillBtn = document.getElementById("backfill-bm-btn");
|
|
||||||
backfillBtn.addEventListener("click", function () {
|
|
||||||
if (!confirm("Download BrandMeister repeater list and add the Brandmeister network tag to all matching repeaters that are missing it?")) return;
|
|
||||||
backfillBtn.disabled = true;
|
|
||||||
backfillBtn.textContent = "Running...";
|
|
||||||
apiFetch("/admin/api/repeaters/backfill-bm", { method: "POST" })
|
|
||||||
.then(function (data) {
|
|
||||||
backfillBtn.disabled = false;
|
|
||||||
backfillBtn.textContent = "Backfill BM";
|
|
||||||
alert("Updated " + data.updated + " repeater" + (data.updated !== 1 ? "s" : "") + ".");
|
|
||||||
if (data.updated > 0) fetchRepeaters();
|
|
||||||
})
|
|
||||||
.catch(function (err) {
|
|
||||||
backfillBtn.disabled = false;
|
|
||||||
backfillBtn.textContent = "Backfill BM";
|
|
||||||
if (err.message !== "unauthorized") alert("Backfill failed: " + err.message);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
searchInput.addEventListener("input", function () {
|
searchInput.addEventListener("input", function () {
|
||||||
clearTimeout(debounceTimer);
|
clearTimeout(debounceTimer);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue