Initialize work order management system with database schema, API handlers, web client, and Docker configuration.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
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})
|
||||
}
|
||||
Reference in New Issue
Block a user