package admin import ( "fmt" "net/http" "os" "path/filepath" "strings" "time" "github.com/gin-gonic/gin" ) var allowedCategories = map[string]bool{ "reports": true, "custody": true, "links": true, } var allowedExts = map[string]bool{ ".pdf": true, ".png": true, ".jpg": true, ".jpeg": true, ".doc": true, ".docx": true, ".txt": true, ".xls": true, ".xlsx": true, } // UploadFile handles multipart file uploads, saves to ./uploads/{category}/, returns accessible URL. // Query param: category (reports|custody), defaults to "reports". func UploadFile(c *gin.Context) { file, err := c.FormFile("file") if err != nil { Fail(c, http.StatusBadRequest, "No file provided") return } ext := strings.ToLower(filepath.Ext(file.Filename)) if !allowedExts[ext] { Fail(c, http.StatusBadRequest, "File type not allowed") return } category := c.DefaultQuery("category", "reports") if !allowedCategories[category] { Fail(c, http.StatusBadRequest, "Invalid category") return } dir := "uploads/" + category if err := os.MkdirAll(dir, 0755); err != nil { Fail(c, http.StatusInternalServerError, "Failed to create upload directory") return } filename := fmt.Sprintf("%d_%s", time.Now().UnixNano(), filepath.Base(file.Filename)) dst := filepath.Join(dir, filename) if err := c.SaveUploadedFile(file, dst); err != nil { Fail(c, http.StatusInternalServerError, "Failed to save file") return } url := "/uploads/" + category + "/" + filename OK(c, gin.H{"url": url}) } // DeleteUploadedFile deletes a previously uploaded file by its URL path. func DeleteUploadedFile(c *gin.Context) { urlPath := c.Query("path") // e.g. /uploads/reports/xxx.pdf or /uploads/custody/xxx.pdf if !strings.HasPrefix(urlPath, "/uploads/") { Fail(c, http.StatusBadRequest, "Invalid file path") return } if strings.Contains(urlPath, "..") { Fail(c, http.StatusBadRequest, "Invalid file path") return } // Verify the second segment is a known category parts := strings.SplitN(strings.TrimPrefix(urlPath, "/uploads/"), "/", 2) if len(parts) != 2 || !allowedCategories[parts[0]] { Fail(c, http.StatusBadRequest, "Invalid file path") return } localPath := strings.TrimPrefix(urlPath, "/") if err := os.Remove(localPath); err != nil { if os.IsNotExist(err) { OK(c, gin.H{"message": "file not found, skipped"}) return } Fail(c, http.StatusInternalServerError, "Failed to delete file") return } OK(c, gin.H{"message": "deleted"}) }