package handlers import ( "fmt" "io" "net/http" "os" "path/filepath" "github.com/google/uuid" "github.com/jmoiron/sqlx" "workorders/internal/config" "workorders/internal/model" ) type AttachmentHandler struct { db *sqlx.DB cfg *config.Config } func NewAttachmentHandler(db *sqlx.DB, cfg *config.Config) *AttachmentHandler { return &AttachmentHandler{db: db, cfg: cfg} } func (h *AttachmentHandler) List(w http.ResponseWriter, r *http.Request) { woID, err := intParam(r, "id") if err != nil { respondError(w, http.StatusBadRequest, "invalid id") return } var atts []model.Attachment if err := h.db.Select(&atts, `SELECT * FROM wo_attachments WHERE wo_id=@p1 ORDER BY uploaded_at`, woID); err != nil { respondError(w, http.StatusInternalServerError, err.Error()) return } for i := range atts { atts[i].URL = "/uploads/" + atts[i].FilePath } respond(w, http.StatusOK, atts) } func (h *AttachmentHandler) Upload(w http.ResponseWriter, r *http.Request) { woID, err := intParam(r, "id") if err != nil { respondError(w, http.StatusBadRequest, "invalid id") return } user := userFromCtx(r) if err := r.ParseMultipartForm(32 << 20); err != nil { respondError(w, http.StatusBadRequest, "parse form failed") return } file, header, err := r.FormFile("file") if err != nil { respondError(w, http.StatusBadRequest, "missing file") return } defer file.Close() ext := filepath.Ext(header.Filename) fname := uuid.New().String() + ext relPath := fmt.Sprintf("%d/%s", woID, fname) absPath := filepath.Join(h.cfg.UploadPath, relPath) if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil { respondError(w, http.StatusInternalServerError, "storage error") return } dst, err := os.Create(absPath) if err != nil { respondError(w, http.StatusInternalServerError, "storage error") return } defer dst.Close() size, _ := io.Copy(dst, file) var aid int err = h.db.QueryRow(` INSERT INTO wo_attachments (wo_id,file_name,file_path,file_type,file_size,caption,phase,uploaded_by) OUTPUT INSERTED.id VALUES (@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8)`, woID, header.Filename, relPath, header.Header.Get("Content-Type"), size, r.FormValue("caption"), r.FormValue("phase"), user.Email, ).Scan(&aid) if err != nil { respondError(w, http.StatusInternalServerError, err.Error()) return } respond(w, http.StatusCreated, map[string]any{ "id": aid, "url": "/uploads/" + relPath, }) } func (h *AttachmentHandler) Delete(w http.ResponseWriter, r *http.Request) { aid, err := intParam(r, "aid") if err != nil { respondError(w, http.StatusBadRequest, "invalid aid") return } var att model.Attachment if err := h.db.Get(&att, `SELECT * FROM wo_attachments WHERE id=@p1`, aid); err == nil { os.Remove(filepath.Join(h.cfg.UploadPath, att.FilePath)) } h.db.Exec(`DELETE FROM wo_attachments WHERE id=@p1`, aid) respond(w, http.StatusOK, map[string]any{"deleted": aid}) }