mirror of
https://github.com/Dvorinka/SEEN.git
synced 2026-06-04 12:33:02 +00:00
169 lines
4.0 KiB
Go
169 lines
4.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/tdvorak/seen/backend/internal/domain"
|
|
"github.com/tdvorak/seen/backend/internal/services/auth"
|
|
"github.com/tdvorak/seen/backend/internal/services/download"
|
|
"github.com/tdvorak/seen/backend/pkg/httpx"
|
|
)
|
|
|
|
type DownloadHandler struct {
|
|
service *download.Service
|
|
authService *auth.Service
|
|
}
|
|
|
|
func NewDownloadHandler(service *download.Service, authService *auth.Service) *DownloadHandler {
|
|
return &DownloadHandler{
|
|
service: service,
|
|
authService: authService,
|
|
}
|
|
}
|
|
|
|
type createDownloadRequest struct {
|
|
SourceType string `json:"sourceType"`
|
|
Source string `json:"source"`
|
|
Title string `json:"title"`
|
|
}
|
|
|
|
func (h *DownloadHandler) Create(c *gin.Context) {
|
|
user, ok := h.resolveUser(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var request createDownloadRequest
|
|
if err := c.ShouldBindJSON(&request); err != nil {
|
|
httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
|
|
return
|
|
}
|
|
|
|
job, err := h.service.Create(c.Request.Context(), user.ID, download.CreateInput{
|
|
SourceType: request.SourceType,
|
|
Source: request.Source,
|
|
Title: request.Title,
|
|
})
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, download.ErrInvalidInput):
|
|
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
|
default:
|
|
httpx.JSONError(c, http.StatusInternalServerError, "failed to create download job")
|
|
}
|
|
return
|
|
}
|
|
|
|
httpx.JSON(c, http.StatusCreated, job)
|
|
}
|
|
|
|
func (h *DownloadHandler) List(c *gin.Context) {
|
|
user, ok := h.resolveUser(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
items, err := h.service.List(c.Request.Context(), user.ID, download.ListParams{
|
|
Status: c.Query("status"),
|
|
Limit: parseIntSafe(c.Query("limit"), 20),
|
|
Offset: parseIntSafe(c.Query("offset"), 0),
|
|
})
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, download.ErrInvalidInput):
|
|
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
|
default:
|
|
httpx.JSONError(c, http.StatusInternalServerError, "failed to list download jobs")
|
|
}
|
|
return
|
|
}
|
|
|
|
httpx.JSON(c, http.StatusOK, items)
|
|
}
|
|
|
|
func (h *DownloadHandler) Cancel(c *gin.Context) {
|
|
user, ok := h.resolveUser(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
jobID := c.Param("id")
|
|
job, err := h.service.Cancel(c.Request.Context(), user.ID, jobID)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, download.ErrInvalidInput):
|
|
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
|
case errors.Is(err, download.ErrNotFound):
|
|
httpx.JSONError(c, http.StatusNotFound, err.Error())
|
|
default:
|
|
httpx.JSONError(c, http.StatusInternalServerError, "failed to cancel download job")
|
|
}
|
|
return
|
|
}
|
|
|
|
httpx.JSON(c, http.StatusOK, job)
|
|
}
|
|
|
|
func (h *DownloadHandler) Events(c *gin.Context) {
|
|
user, ok := h.resolveUser(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
jobID := c.Param("id")
|
|
items, err := h.service.Events(c.Request.Context(), user.ID, jobID, download.EventParams{
|
|
After: c.Query("after"),
|
|
Limit: parseIntSafe(c.Query("limit"), 100),
|
|
})
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, download.ErrInvalidInput):
|
|
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
|
case errors.Is(err, download.ErrNotFound):
|
|
httpx.JSONError(c, http.StatusNotFound, err.Error())
|
|
default:
|
|
httpx.JSONError(c, http.StatusInternalServerError, "failed to list download events")
|
|
}
|
|
return
|
|
}
|
|
|
|
httpx.JSON(c, http.StatusOK, items)
|
|
}
|
|
|
|
func (h *DownloadHandler) resolveUser(c *gin.Context) (*domain.User, bool) {
|
|
accessToken, ok := bearerToken(c.GetHeader("Authorization"))
|
|
if !ok {
|
|
httpx.JSONError(c, http.StatusUnauthorized, "missing bearer token")
|
|
return nil, false
|
|
}
|
|
|
|
user, err := h.authService.UserFromAccessToken(c.Request.Context(), accessToken)
|
|
if err != nil {
|
|
switch {
|
|
case errors.Is(err, auth.ErrInvalidToken):
|
|
httpx.JSONError(c, http.StatusUnauthorized, err.Error())
|
|
default:
|
|
httpx.JSONError(c, http.StatusInternalServerError, "failed to resolve user")
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
return user, true
|
|
}
|
|
|
|
func parseIntSafe(raw string, fallback int) int {
|
|
if raw == "" {
|
|
return fallback
|
|
}
|
|
|
|
parsed, err := strconv.Atoi(raw)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
|
|
return parsed
|
|
}
|