90a96b4125
- Remove api_es service, Dockerfile, all Go source files - Remove api_es from docker-compose.yml, nginx-ssl.conf, .env, Makefile - Replace nginx /api/ proxy with /api/v1/ → api_yal:8787 - Add amenity/upload domains, AuthResponse, GET /auth/me, GET /objects/my to api_yal - Rewrite easysite frontend: types, composables, and all 5 pages to use api_yal DTOs - Wire nuxt.config public.apiBase, add useObjects CRUD composable - Update docs references from api_es to api_yal
151 lines
3.6 KiB
Go
151 lines
3.6 KiB
Go
package upload
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"api_yal/internal/logger"
|
|
"api_yal/internal/middleware"
|
|
"api_yal/internal/models"
|
|
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type UploadHandler struct {
|
|
db *gorm.DB
|
|
uploadPath string
|
|
}
|
|
|
|
func NewHandler(db *gorm.DB, uploadPath string) *UploadHandler {
|
|
if err := os.MkdirAll(uploadPath, 0755); err != nil {
|
|
logger.Get().Warn("failed to create upload directory", zap.String("path", uploadPath), zap.Error(err))
|
|
}
|
|
return &UploadHandler{db: db, uploadPath: uploadPath}
|
|
}
|
|
|
|
func (h *UploadHandler) Upload(w http.ResponseWriter, r *http.Request) {
|
|
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
|
if !ok {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
if err := r.ParseMultipartForm(32 << 20); err != nil {
|
|
http.Error(w, "Failed to parse form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
file, header, err := r.FormFile("file")
|
|
if err != nil {
|
|
http.Error(w, "File is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
objectIDStr := r.FormValue("object_id")
|
|
if objectIDStr == "" {
|
|
http.Error(w, "object_id is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var objectID uint
|
|
if _, err := fmt.Sscan(objectIDStr, &objectID); err != nil {
|
|
http.Error(w, "Invalid object_id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
ext := filepath.Ext(header.Filename)
|
|
filename := fmt.Sprintf("%d_%d%s", userID, time.Now().UnixNano(), ext)
|
|
filePath := filepath.Join(h.uploadPath, filename)
|
|
|
|
dst, err := os.Create(filePath)
|
|
if err != nil {
|
|
logger.Get().Error("failed to create file", zap.Error(err))
|
|
http.Error(w, "Failed to save file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer dst.Close()
|
|
|
|
if _, err := io.Copy(dst, file); err != nil {
|
|
logger.Get().Error("failed to write file", zap.Error(err))
|
|
http.Error(w, "Failed to save file", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
isPrimary := false
|
|
if r.FormValue("is_primary") == "true" {
|
|
isPrimary = true
|
|
}
|
|
|
|
image := models.ObjectImage{
|
|
ObjectID: objectID,
|
|
URL: "/uploads/" + filename,
|
|
IsPrimary: isPrimary,
|
|
}
|
|
|
|
if err := h.db.Create(&image).Error; err != nil {
|
|
logger.Get().Error("failed to save image record", zap.Error(err))
|
|
http.Error(w, "Failed to save image", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"id": image.ID,
|
|
"url": image.URL,
|
|
"object_id": image.ObjectID,
|
|
"is_primary": image.IsPrimary,
|
|
})
|
|
}
|
|
|
|
func (h *UploadHandler) DeleteImage(w http.ResponseWriter, r *http.Request) {
|
|
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
|
if !ok {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
imageIDStr := r.URL.Query().Get("id")
|
|
if imageIDStr == "" {
|
|
http.Error(w, "id is required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var imageID uint
|
|
if _, err := fmt.Sscan(imageIDStr, &imageID); err != nil {
|
|
http.Error(w, "Invalid id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var image models.ObjectImage
|
|
if err := h.db.First(&image, imageID).Error; err != nil {
|
|
http.Error(w, "Image not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
var object models.Object
|
|
if err := h.db.First(&object, image.ObjectID).Error; err != nil {
|
|
http.Error(w, "Object not found", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if object.OwnerID != userID {
|
|
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
return
|
|
}
|
|
|
|
filePath := filepath.Join(h.uploadPath, filepath.Base(image.URL))
|
|
os.Remove(filePath)
|
|
|
|
h.db.Delete(&image)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(map[string]string{"message": "Image deleted"})
|
|
}
|