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) }