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