184 lines
4.5 KiB
Go
184 lines
4.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/muyuqingfeng/iloom/auth-service/internal/repository"
|
|
"github.com/muyuqingfeng/iloom/auth-service/internal/storage"
|
|
"github.com/muyuqingfeng/iloom/shared/pkg/response"
|
|
)
|
|
|
|
type CommonHandler struct {
|
|
shareLinkRepo *repository.ShareLinkRepo
|
|
auditLogRepo *repository.AuditLogRepo
|
|
storage *storage.MinIOStorage
|
|
}
|
|
|
|
func NewCommonHandler(slRepo *repository.ShareLinkRepo, alRepo *repository.AuditLogRepo, st *storage.MinIOStorage) *CommonHandler {
|
|
return &CommonHandler{shareLinkRepo: slRepo, auditLogRepo: alRepo, storage: st}
|
|
}
|
|
|
|
type createShareLinkReq struct {
|
|
ResourceType string `json:"resource_type" binding:"required"`
|
|
ResourceID string `json:"resource_id" binding:"required"`
|
|
ExpiresIn int `json:"expires_in"` // seconds, 0 = no expiry
|
|
}
|
|
|
|
func (h *CommonHandler) CreateShareLink(c *gin.Context) {
|
|
var req createShareLinkReq
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "invalid request: "+err.Error())
|
|
return
|
|
}
|
|
|
|
userID := c.GetHeader("X-User-ID")
|
|
companyID := c.GetHeader("X-Company-ID")
|
|
|
|
var expiresAt *time.Time
|
|
if req.ExpiresIn > 0 {
|
|
t := time.Now().Add(time.Duration(req.ExpiresIn) * time.Second)
|
|
expiresAt = &t
|
|
}
|
|
|
|
link, err := h.shareLinkRepo.Create(c.Request.Context(), companyID, userID, req.ResourceType, req.ResourceID, expiresAt)
|
|
if err != nil {
|
|
response.InternalError(c, err.Error())
|
|
return
|
|
}
|
|
|
|
response.OK(c, link)
|
|
}
|
|
|
|
func (h *CommonHandler) GetShareLink(c *gin.Context) {
|
|
code := c.Param("code")
|
|
|
|
link, err := h.shareLinkRepo.GetByCode(c.Request.Context(), code)
|
|
if err != nil {
|
|
response.InternalError(c, err.Error())
|
|
return
|
|
}
|
|
if link == nil {
|
|
response.NotFound(c, "share link not found")
|
|
return
|
|
}
|
|
|
|
// check expiry
|
|
if link.ExpiresAt != nil && link.ExpiresAt.Before(time.Now()) {
|
|
response.BadRequest(c, "share link expired")
|
|
return
|
|
}
|
|
|
|
response.OK(c, link)
|
|
}
|
|
|
|
type feedbackReq struct {
|
|
Content string `json:"content" binding:"required"`
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
func (h *CommonHandler) Feedback(c *gin.Context) {
|
|
var req feedbackReq
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "invalid request: "+err.Error())
|
|
return
|
|
}
|
|
|
|
userID := c.GetHeader("X-User-ID")
|
|
companyID := c.GetHeader("X-Company-ID")
|
|
|
|
feedbackType := req.Type
|
|
if feedbackType == "" {
|
|
feedbackType = "general"
|
|
}
|
|
|
|
err := h.auditLogRepo.Create(
|
|
c.Request.Context(), userID, companyID,
|
|
"feedback", feedbackType, "",
|
|
c.ClientIP(), map[string]string{"content": req.Content},
|
|
)
|
|
if err != nil {
|
|
response.InternalError(c, err.Error())
|
|
return
|
|
}
|
|
|
|
response.OK(c, gin.H{"message": "feedback received"})
|
|
}
|
|
|
|
type crashReportReq struct {
|
|
Error string `json:"error" binding:"required"`
|
|
StackTrace string `json:"stack_trace"`
|
|
UserAgent string `json:"user_agent"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
func (h *CommonHandler) CrashReport(c *gin.Context) {
|
|
var req crashReportReq
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.BadRequest(c, "invalid request: "+err.Error())
|
|
return
|
|
}
|
|
|
|
userID := c.GetHeader("X-User-ID")
|
|
companyID := c.GetHeader("X-Company-ID")
|
|
|
|
err := h.auditLogRepo.Create(
|
|
c.Request.Context(), userID, companyID,
|
|
"crash_report", "frontend", "",
|
|
c.ClientIP(), req,
|
|
)
|
|
if err != nil {
|
|
response.InternalError(c, err.Error())
|
|
return
|
|
}
|
|
|
|
response.OK(c, gin.H{"message": "crash report received"})
|
|
}
|
|
|
|
const maxUploadSize = 10 << 20 // 10 MB
|
|
|
|
func (h *CommonHandler) Upload(c *gin.Context) {
|
|
if h.storage == nil {
|
|
response.InternalError(c, "file storage not configured")
|
|
return
|
|
}
|
|
|
|
if err := c.Request.ParseMultipartForm(maxUploadSize); err != nil {
|
|
response.BadRequest(c, fmt.Sprintf("file too large (max %dMB)", maxUploadSize>>20))
|
|
return
|
|
}
|
|
|
|
file, header, err := c.Request.FormFile("file")
|
|
if err != nil {
|
|
response.BadRequest(c, "missing file field")
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
// Validate content type
|
|
contentType := header.Header.Get("Content-Type")
|
|
if !strings.HasPrefix(contentType, "image/") {
|
|
response.BadRequest(c, "only image files are allowed")
|
|
return
|
|
}
|
|
|
|
// Validate size
|
|
if header.Size > maxUploadSize {
|
|
response.BadRequest(c, fmt.Sprintf("file too large (max %dMB)", maxUploadSize>>20))
|
|
return
|
|
}
|
|
|
|
pathPrefix := c.DefaultPostForm("path_prefix", "products")
|
|
|
|
objectPath, err := h.storage.Upload(c.Request.Context(), pathPrefix, header.Filename, file, header.Size, contentType)
|
|
if err != nil {
|
|
response.InternalError(c, "upload failed: "+err.Error())
|
|
return
|
|
}
|
|
|
|
url := "/uploads/" + objectPath
|
|
response.OK(c, gin.H{"url": url})
|
|
}
|