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.
 
 
 
 
 
 

151 lines
3.8 KiB

package handlers
import (
"database/sql"
"encoding/json"
"log/slog"
"net/http"
"os"
"strconv"
"person-home/internal/db"
"person-home/internal/models"
"person-home/internal/storage"
)
type AdminHandler struct {
DB *sql.DB
UploadDir string
}
func (h *AdminHandler) List(w http.ResponseWriter, r *http.Request) {
projects, err := db.ListProjects(h.DB, "", false, 0)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to list projects")
return
}
if projects == nil {
projects = []models.Project{}
}
writeJSON(w, http.StatusOK, projects)
}
func (h *AdminHandler) Create(w http.ResponseWriter, r *http.Request) {
var req models.CreateProjectRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
if req.Title == "" {
writeError(w, http.StatusBadRequest, "title is required")
return
}
if req.Tags == nil {
req.Tags = []string{}
}
project, err := db.CreateProject(h.DB, &req)
if err != nil {
slog.Error("create project", "error", err)
writeError(w, http.StatusInternalServerError, "failed to create project")
return
}
writeJSON(w, http.StatusCreated, project)
}
func (h *AdminHandler) Update(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
writeError(w, http.StatusBadRequest, "invalid id")
return
}
var req models.UpdateProjectRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
project, err := db.UpdateProject(h.DB, id, &req)
if err != nil {
slog.Error("update project", "error", err)
writeError(w, http.StatusInternalServerError, "failed to update project")
return
}
if project == nil {
writeError(w, http.StatusNotFound, "project not found")
return
}
writeJSON(w, http.StatusOK, project)
}
func (h *AdminHandler) Delete(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
writeError(w, http.StatusBadRequest, "invalid id")
return
}
// Retrieve image path before deletion for cleanup
project, _ := db.GetProject(h.DB, id)
if project != nil && project.Image != "" {
os.Remove(h.UploadDir + "/" + project.Image)
}
if err := db.DeleteProject(h.DB, id); err != nil {
if err == sql.ErrNoRows {
writeError(w, http.StatusNotFound, "project not found")
return
}
slog.Error("delete project", "error", err)
writeError(w, http.StatusInternalServerError, "failed to delete project")
return
}
w.WriteHeader(http.StatusNoContent)
}
func (h *AdminHandler) Export(w http.ResponseWriter, r *http.Request) {
projects, err := db.ListProjects(h.DB, "", false, 0)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to export projects")
return
}
if projects == nil {
projects = []models.Project{}
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Disposition", "attachment; filename=projects.json")
json.NewEncoder(w).Encode(projects)
}
func (h *AdminHandler) Upload(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
if err := r.ParseMultipartForm(10 << 20); err != nil {
writeError(w, http.StatusBadRequest, "file too large or invalid form data")
return
}
file, header, err := r.FormFile("image")
if err != nil {
writeError(w, http.StatusBadRequest, "missing image file")
return
}
defer file.Close()
filename, err := storage.SaveImage(file, header, h.UploadDir)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to save image")
return
}
writeJSON(w, http.StatusOK, map[string]string{
"url": "/uploads/" + filename,
"filename": filename,
})
}