You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
3.1 KiB
109 lines
3.1 KiB
package main
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// HandleListLinks handles GET /api/admin/links — returns all records.
|
|
func (h *Handler) HandleListLinks(w http.ResponseWriter, r *http.Request) {
|
|
records, err := h.store.ListAll()
|
|
if err != nil {
|
|
log.Printf("list links error: %v", err)
|
|
jsonError(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(records)
|
|
}
|
|
|
|
// HandleUpdateLink handles PUT /api/admin/links/{code} — updates a link's URL.
|
|
func (h *Handler) HandleUpdateLink(w http.ResponseWriter, r *http.Request) {
|
|
code := r.PathValue("code")
|
|
if code == "" {
|
|
jsonError(w, "missing short code", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
URL string `json:"url"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
jsonError(w, "invalid request body", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
req.URL = strings.TrimSpace(req.URL)
|
|
if req.URL == "" {
|
|
jsonError(w, "url is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if !strings.HasPrefix(req.URL, "http://") && !strings.HasPrefix(req.URL, "https://") {
|
|
jsonError(w, "url must start with http:// or https://", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := h.store.Update(code, req.URL); err != nil {
|
|
log.Printf("update link error: %v", err)
|
|
jsonError(w, "short link not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
rec, _ := h.store.FindByCode(code)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(rec)
|
|
}
|
|
|
|
// HandleDeleteLink handles DELETE /api/admin/links/{code} — deletes a link.
|
|
func (h *Handler) HandleDeleteLink(w http.ResponseWriter, r *http.Request) {
|
|
code := r.PathValue("code")
|
|
if code == "" {
|
|
jsonError(w, "missing short code", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := h.store.Delete(code); err != nil {
|
|
log.Printf("delete link error: %v", err)
|
|
jsonError(w, "short link not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(map[string]bool{"ok": true})
|
|
}
|
|
|
|
// HandleExport handles GET /api/admin/export?format=json|csv — exports all data.
|
|
func (h *Handler) HandleExport(w http.ResponseWriter, r *http.Request) {
|
|
records, err := h.store.ListAll()
|
|
if err != nil {
|
|
log.Printf("export error: %v", err)
|
|
jsonError(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
format := r.URL.Query().Get("format")
|
|
if format == "csv" {
|
|
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
|
|
w.Header().Set("Content-Disposition", "attachment; filename=\"shortlinks.csv\"")
|
|
wr := csv.NewWriter(w)
|
|
wr.Write([]string{"code", "original_url", "visit_count", "created_at"})
|
|
for _, rec := range records {
|
|
wr.Write([]string{
|
|
rec.Code,
|
|
rec.OriginalURL,
|
|
strconv.FormatInt(rec.VisitCount, 10),
|
|
rec.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
|
})
|
|
}
|
|
wr.Flush()
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Content-Disposition", "attachment; filename=\"shortlinks.json\"")
|
|
json.NewEncoder(w).Encode(records)
|
|
}
|
|
|