mirror of
https://github.com/Dvorinka/SEEN.git
synced 2026-06-04 12:33:02 +00:00
small fix, don't worry about it
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tdvorak/seen/backend/internal/services/auth"
|
||||
"github.com/tdvorak/seen/backend/pkg/httpx"
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
service *auth.Service
|
||||
}
|
||||
|
||||
func NewAuthHandler(service *auth.Service) *AuthHandler {
|
||||
return &AuthHandler{service: service}
|
||||
}
|
||||
|
||||
type registerRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
DisplayName string `json:"displayName"`
|
||||
}
|
||||
|
||||
type loginRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type refreshRequest struct {
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Me(c *gin.Context) {
|
||||
accessToken, ok := bearerToken(c.GetHeader("Authorization"))
|
||||
if !ok {
|
||||
httpx.JSONError(c, http.StatusUnauthorized, "missing bearer token")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.service.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
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Register(c *gin.Context) {
|
||||
var request registerRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.Register(c.Request.Context(), auth.RegisterInput{
|
||||
Email: request.Email,
|
||||
Password: request.Password,
|
||||
DisplayName: request.DisplayName,
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, auth.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
case errors.Is(err, auth.ErrEmailTaken):
|
||||
httpx.JSONError(c, http.StatusConflict, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "register failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusCreated, result)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var request loginRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.Login(c.Request.Context(), auth.LoginInput{
|
||||
Email: request.Email,
|
||||
Password: request.Password,
|
||||
UserAgent: c.GetHeader("User-Agent"),
|
||||
IP: c.ClientIP(),
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, auth.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
case errors.Is(err, auth.ErrInvalidCredentials):
|
||||
httpx.JSONError(c, http.StatusUnauthorized, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "login failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Refresh(c *gin.Context) {
|
||||
var request refreshRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.Refresh(c.Request.Context(), auth.RefreshInput{
|
||||
RefreshToken: request.RefreshToken,
|
||||
UserAgent: c.GetHeader("User-Agent"),
|
||||
IP: c.ClientIP(),
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, auth.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
case errors.Is(err, auth.ErrInvalidSession):
|
||||
httpx.JSONError(c, http.StatusUnauthorized, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "refresh failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, result)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package handlers
|
||||
|
||||
import "strings"
|
||||
|
||||
func bearerToken(header string) (string, bool) {
|
||||
trimmed := strings.TrimSpace(header)
|
||||
if trimmed == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
parts := strings.SplitN(trimmed, " ", 2)
|
||||
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
|
||||
return "", false
|
||||
}
|
||||
|
||||
token := strings.TrimSpace(parts[1])
|
||||
if token == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return token, true
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
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/catalog"
|
||||
"github.com/tdvorak/seen/backend/pkg/httpx"
|
||||
)
|
||||
|
||||
type CatalogHandler struct {
|
||||
service *catalog.Service
|
||||
authService *auth.Service
|
||||
}
|
||||
|
||||
func NewCatalogHandler(service *catalog.Service, authService *auth.Service) *CatalogHandler {
|
||||
return &CatalogHandler{service: service, authService: authService}
|
||||
}
|
||||
|
||||
type watchLaterAddRequest struct {
|
||||
MediaID int `json:"mediaId"`
|
||||
}
|
||||
|
||||
type progressUpdateRequest struct {
|
||||
MediaID int `json:"mediaId"`
|
||||
SeasonNumber int `json:"seasonNumber"`
|
||||
EpisodeNumber int `json:"episodeNumber"`
|
||||
ProgressPercent int `json:"progressPercent"`
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) Dashboard(c *gin.Context) {
|
||||
httpx.JSON(c, http.StatusOK, h.service.Dashboard())
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) ContinueWatching(c *gin.Context) {
|
||||
user, ok := h.resolveUser(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
items, err := h.service.ContinueWatching(user.ID)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, catalog.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "failed to load continue watching")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, items)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) Discover(c *gin.Context) {
|
||||
page := parseInt(c.Query("page"), 1)
|
||||
pageSize := parseInt(c.Query("pageSize"), 6)
|
||||
|
||||
httpx.JSON(c, http.StatusOK, h.service.Discover(catalog.DiscoverParams{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Query: c.Query("query"),
|
||||
Genre: c.Query("genre"),
|
||||
MediaType: c.Query("mediaType"),
|
||||
}))
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) Games(c *gin.Context) {
|
||||
page := parseInt(c.Query("page"), 1)
|
||||
pageSize := parseInt(c.Query("pageSize"), 6)
|
||||
|
||||
httpx.JSON(c, http.StatusOK, h.service.Discover(catalog.DiscoverParams{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Query: c.Query("query"),
|
||||
Genre: c.Query("genre"),
|
||||
MediaType: string(catalog.MediaTypeGame),
|
||||
}))
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) Search(c *gin.Context) {
|
||||
httpx.JSON(c, http.StatusOK, h.service.Search(catalog.SearchParams{
|
||||
Query: c.Query("query"),
|
||||
Genre: c.Query("genre"),
|
||||
MediaType: c.Query("mediaType"),
|
||||
}))
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) WatchLater(c *gin.Context) {
|
||||
user, ok := h.resolveUser(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
items, err := h.service.WatchLater(user.ID)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, catalog.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "failed to load watch later")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, items)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) AddWatchLater(c *gin.Context) {
|
||||
user, ok := h.resolveUser(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var request watchLaterAddRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
items, err := h.service.AddWatchLater(user.ID, request.MediaID)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, catalog.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
case errors.Is(err, catalog.ErrMediaNotFound):
|
||||
httpx.JSONError(c, http.StatusNotFound, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "failed to add watch later item")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, items)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) RemoveWatchLater(c *gin.Context) {
|
||||
user, ok := h.resolveUser(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
mediaID := parseInt(c.Param("mediaId"), 0)
|
||||
items, err := h.service.RemoveWatchLater(user.ID, mediaID)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, catalog.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "failed to remove watch later item")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, items)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) UpdateProgress(c *gin.Context) {
|
||||
user, ok := h.resolveUser(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var request progressUpdateRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
items, err := h.service.UpdateProgress(user.ID, catalog.ProgressUpdateInput{
|
||||
MediaID: request.MediaID,
|
||||
SeasonNumber: request.SeasonNumber,
|
||||
EpisodeNumber: request.EpisodeNumber,
|
||||
ProgressPercent: request.ProgressPercent,
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, catalog.ErrInvalidInput):
|
||||
httpx.JSONError(c, http.StatusBadRequest, err.Error())
|
||||
case errors.Is(err, catalog.ErrMediaNotFound):
|
||||
httpx.JSONError(c, http.StatusNotFound, err.Error())
|
||||
default:
|
||||
httpx.JSONError(c, http.StatusInternalServerError, "failed to update progress")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
httpx.JSON(c, http.StatusOK, items)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) 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 parseInt(raw string, fallback int) int {
|
||||
if raw == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
parsed, err := strconv.Atoi(raw)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return parsed
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type HealthHandler struct {
|
||||
db *pgxpool.Pool
|
||||
cache *redis.Client
|
||||
}
|
||||
|
||||
func NewHealthHandler(db *pgxpool.Pool, cache *redis.Client) *HealthHandler {
|
||||
return &HealthHandler{db: db, cache: cache}
|
||||
}
|
||||
|
||||
func (h *HealthHandler) Live(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok", "timestamp": time.Now().UTC()})
|
||||
}
|
||||
|
||||
func (h *HealthHandler) Ready(c *gin.Context) {
|
||||
ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
health := gin.H{"status": "ready"}
|
||||
|
||||
if err := h.db.Ping(ctx); err != nil {
|
||||
health["status"] = "degraded"
|
||||
health["postgres"] = err.Error()
|
||||
c.JSON(http.StatusServiceUnavailable, health)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.cache.Ping(ctx).Err(); err != nil {
|
||||
health["status"] = "degraded"
|
||||
health["dragonfly"] = err.Error()
|
||||
c.JSON(http.StatusServiceUnavailable, health)
|
||||
return
|
||||
}
|
||||
|
||||
health["postgres"] = "ok"
|
||||
health["dragonfly"] = "ok"
|
||||
c.JSON(http.StatusOK, health)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type PlaceholderHandler struct{}
|
||||
|
||||
func NewPlaceholderHandler() *PlaceholderHandler {
|
||||
return &PlaceholderHandler{}
|
||||
}
|
||||
|
||||
func (h *PlaceholderHandler) NotImplemented(feature string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.JSON(http.StatusNotImplemented, gin.H{
|
||||
"error": "not implemented",
|
||||
"feature": feature,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tdvorak/seen/backend/internal/api/handlers"
|
||||
)
|
||||
|
||||
func Register(
|
||||
router *gin.RouterGroup,
|
||||
health *handlers.HealthHandler,
|
||||
auth *handlers.AuthHandler,
|
||||
catalog *handlers.CatalogHandler,
|
||||
download *handlers.DownloadHandler,
|
||||
placeholder *handlers.PlaceholderHandler,
|
||||
) {
|
||||
router.GET("/health/live", health.Live)
|
||||
router.GET("/health/ready", health.Ready)
|
||||
|
||||
authGroup := router.Group("/auth")
|
||||
authGroup.POST("/register", auth.Register)
|
||||
authGroup.POST("/login", auth.Login)
|
||||
authGroup.POST("/refresh", auth.Refresh)
|
||||
authGroup.GET("/me", auth.Me)
|
||||
|
||||
router.GET("/dashboard", catalog.Dashboard)
|
||||
router.GET("/progress/continue-watching", catalog.ContinueWatching)
|
||||
router.GET("/discover", catalog.Discover)
|
||||
router.GET("/games", catalog.Games)
|
||||
router.GET("/search", catalog.Search)
|
||||
router.GET("/watch-later", catalog.WatchLater)
|
||||
router.POST("/watch-later", catalog.AddWatchLater)
|
||||
router.DELETE("/watch-later/:mediaId", catalog.RemoveWatchLater)
|
||||
router.POST("/progress", catalog.UpdateProgress)
|
||||
|
||||
router.GET("/movies", placeholder.NotImplemented("movies"))
|
||||
router.GET("/shows", placeholder.NotImplemented("shows"))
|
||||
router.GET("/watched", placeholder.NotImplemented("watched"))
|
||||
router.GET("/watchlist", placeholder.NotImplemented("watchlist"))
|
||||
router.GET("/progress", placeholder.NotImplemented("progress"))
|
||||
router.POST("/downloads", download.Create)
|
||||
router.GET("/downloads", download.List)
|
||||
router.DELETE("/downloads/:id", download.Cancel)
|
||||
router.GET("/downloads/:id/events", download.Events)
|
||||
router.GET("/calendar", placeholder.NotImplemented("calendar"))
|
||||
router.GET("/library", placeholder.NotImplemented("library"))
|
||||
router.GET("/collections", placeholder.NotImplemented("collections"))
|
||||
router.GET("/settings", placeholder.NotImplemented("settings"))
|
||||
router.GET("/admin", placeholder.NotImplemented("admin"))
|
||||
router.GET("/recommendations", placeholder.NotImplemented("recommendations"))
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tdvorak/seen/backend/internal/api/handlers"
|
||||
v1 "github.com/tdvorak/seen/backend/internal/api/routes/v1"
|
||||
"github.com/tdvorak/seen/backend/internal/config"
|
||||
"github.com/tdvorak/seen/backend/internal/middleware"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Handlers struct {
|
||||
Health *handlers.HealthHandler
|
||||
Auth *handlers.AuthHandler
|
||||
Catalog *handlers.CatalogHandler
|
||||
Download *handlers.DownloadHandler
|
||||
Placeholder *handlers.PlaceholderHandler
|
||||
}
|
||||
|
||||
func NewRouter(cfg config.Config, log *zap.Logger, handlers Handlers) *gin.Engine {
|
||||
if cfg.Env == "production" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
engine := gin.New()
|
||||
engine.Use(middleware.RequestID())
|
||||
engine.Use(middleware.CORS(cfg.CORS))
|
||||
engine.Use(middleware.AccessLog(log))
|
||||
engine.Use(middleware.Recovery(log))
|
||||
|
||||
engine.GET("/healthz", handlers.Health.Live)
|
||||
|
||||
apiV1 := engine.Group("/api/v1")
|
||||
v1.Register(apiV1, handlers.Health, handlers.Auth, handlers.Catalog, handlers.Download, handlers.Placeholder)
|
||||
|
||||
return engine
|
||||
}
|
||||
Reference in New Issue
Block a user