- PostgreSQL metadata overlay layer on top of existing S3 storage - 3 new tables: folders, files, share_links - Folder CRUD: create, get with children, tree, rename, delete (cascade) - File operations: upload to folder, move between folders - Share links: create with optional password/expiry/download limit, public access - S3 compensation on PG write failure - Existing 14 endpoints untouched
104 lines
2.9 KiB
Go
104 lines
2.9 KiB
Go
package endpoints
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"rag/file-system/internal/api/handlers"
|
|
"rag/file-system/internal/api/requests"
|
|
"rag/file-system/internal/api/validators"
|
|
"rag/file-system/internal/domain/model"
|
|
"rag/file-system/internal/infrastructure/mediator"
|
|
"rag/file-system/internal/middleware"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type ShareEndpoint struct {
|
|
Mediator *mediator.Mediator
|
|
ShareValidator *validators.ShareValidator
|
|
}
|
|
|
|
func NewShareEndpoint(m *mediator.Mediator, sv *validators.ShareValidator) *ShareEndpoint {
|
|
return &ShareEndpoint{Mediator: m, ShareValidator: sv}
|
|
}
|
|
|
|
func (e *ShareEndpoint) CreateShare(c *gin.Context) {
|
|
var req requests.CreateShareRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if err := e.ShareValidator.ValidateCreate(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ownerID := c.GetString(middleware.ContextKeyUserID)
|
|
|
|
var expiresAt *time.Time
|
|
if req.ExpiresAt != nil && *req.ExpiresAt != "" {
|
|
t, err := time.Parse(time.RFC3339, *req.ExpiresAt)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "过期时间格式错误,请使用 ISO 8601 格式"})
|
|
return
|
|
}
|
|
expiresAt = &t
|
|
}
|
|
|
|
cmd := handlers.CreateShareCommand{
|
|
ResourceType: req.ResourceType,
|
|
ResourceID: req.ResourceID,
|
|
Password: req.Password,
|
|
ExpiresAt: expiresAt,
|
|
MaxDownloads: req.MaxDownloads,
|
|
CreatedBy: ownerID,
|
|
}
|
|
|
|
result, err := mediator.Send[handlers.CreateShareCommand, *model.ShareLink](e.Mediator, c.Request.Context(), cmd)
|
|
if err != nil {
|
|
handleError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": result})
|
|
}
|
|
|
|
func (e *ShareEndpoint) GetShareInfo(c *gin.Context) {
|
|
token := c.Param("token")
|
|
|
|
query := handlers.GetShareInfoQuery{Token: token}
|
|
result, err := mediator.Send[handlers.GetShareInfoQuery, *model.ShareInfo](e.Mediator, c.Request.Context(), query)
|
|
if err != nil {
|
|
handleError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"data": result})
|
|
}
|
|
|
|
func (e *ShareEndpoint) DownloadShare(c *gin.Context) {
|
|
token := c.Param("token")
|
|
var req requests.ShareDownloadRequest
|
|
_ = c.ShouldBindJSON(&req)
|
|
|
|
query := handlers.DownloadShareQuery{Token: token, Password: req.Password}
|
|
result, err := mediator.Send[handlers.DownloadShareQuery, string](e.Mediator, c.Request.Context(), query)
|
|
if err != nil {
|
|
handleError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"url": result})
|
|
}
|
|
|
|
func (e *ShareEndpoint) DeleteShare(c *gin.Context) {
|
|
shareID := c.Param("id")
|
|
ownerID := c.GetString(middleware.ContextKeyUserID)
|
|
|
|
cmd := handlers.DeleteShareCommand{ShareID: shareID, CreatedBy: ownerID}
|
|
result, err := mediator.Send[handlers.DeleteShareCommand, string](e.Mediator, c.Request.Context(), cmd)
|
|
if err != nil {
|
|
handleError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": result})
|
|
}
|