shelfy-v2/internal/routes/routes.go
2025-09-28 14:04:59 +02:00

204 lines
6.4 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package routes
import (
"canguidev/shelfy/internal/controllers"
"canguidev/shelfy/internal/middlewares"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func AddRoutes(superRoute *gin.RouterGroup,db *gorm.DB) {
userRoutes(superRoute,db)
testAcces(superRoute)
loginRoutes(superRoute,db)
pathRoute(superRoute,db)
folderRoute(superRoute,db)
downloadRoute(superRoute,db)
}
func loginRoutes(superRoute *gin.RouterGroup,db *gorm.DB) {
loginRouter := superRoute.Group("/login")
{
loginRouter.POST("/", controllers.Login(db))
}
}
func userRoutes( superRoute *gin.RouterGroup,db*gorm.DB){
userRoutes := superRoute.Group("/users")
{
userRoutes.POST("/",middlewares.CheckAuth(db),controllers.CreateUser(db))
userRoutes.GET("/", middlewares.CheckAuth(db),controllers.ReadAllUser(db))
userRoutes.GET("/:id", middlewares.CheckAuth(db),controllers.FindUserById(db))
userRoutes.PUT("/:id", middlewares.CheckAuth(db),controllers.UpdateUser(db))
userRoutes.DELETE("/:id", middlewares.CheckAuth(db),controllers.DeleteUser(db))
}
}
func testAcces(superRoute *gin.RouterGroup){
test := superRoute.Group("/test")
{
test.GET("/",controllers.TestPing())
}
}
func pathRoute (superRoute *gin.RouterGroup,db *gorm.DB){
pathRoutes := superRoute.Group("/path")
{
pathRoutes.POST("/save-path", middlewares.CheckAuth(db),controllers.CreateSavePath(db))
pathRoutes.GET("/save-path", middlewares.CheckAuth(db),controllers.ReadAllSavePath(db))
pathRoutes.PUT("/save-path/:id", middlewares.CheckAuth(db),controllers.UpdateSavePath(db))
pathRoutes.DELETE("/save-path/:id", middlewares.CheckAuth(db),controllers.DeleteSavePath(db))
pathRoutes.POST("/save-path/validate", middlewares.CheckAuth(db),controllers.PathValidationHandler)
}
}
func folderRoute (superRoute *gin.RouterGroup,db *gorm.DB){
folderRoutes :=superRoute.Group("/folders")
{
folderRoutes.GET("/",middlewares.CheckAuth(db),controllers.StreamHandler(db))
folderRoutes.GET("/details",middlewares.CheckAuth(db),controllers.DetailHandler())
folderRoutes.DELETE("", middlewares.CheckAuth(db), controllers.DeleteHandler())
folderRoutes.DELETE("/", middlewares.CheckAuth(db), controllers.DeleteHandler())
folderRoutes.GET("/stats", middlewares.CheckAuth(db), controllers.FolderStatsHandler())
folderRoutes.GET("/file", middlewares.CheckAuth(db), func(c *gin.Context) {
path := c.Query("path")
filePath := filepath.Join("upload", filepath.Clean("/"+path))
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
c.Header("Pragma", "no-cache")
c.Header("Expires", "0")
// Supprime le support de "If-Modified-Since" pour forcer le 200
c.Request.Header.Del("If-Modified-Since")
ext := strings.ToLower(filepath.Ext(filePath))
if ext == ".mp4" || ext == ".mkv" || ext == ".webm" || ext == ".mp3" {
streamFile(c, filePath)
} else {
c.File(filePath)
}
})
}
}
func downloadRoute (superRoute *gin.RouterGroup,db *gorm.DB){
downloadRoutes := superRoute.Group("/download")
{
downloadRoutes.GET("/jobs", middlewares.CheckAuth(db), controllers.HandleListJobs(db))
downloadRoutes.GET("/debrid-status",middlewares.CheckAuth(db),controllers.GetDebridStatus(db))
downloadRoutes.POST("/account", middlewares.CheckAuth(db),controllers.PostDebridLogin(db))
downloadRoutes.POST("/add", middlewares.CheckAuth(db),controllers.HandleAddJobsMultiple(db))
downloadRoutes.POST("/jobs/:id/start", middlewares.CheckAuth(db),controllers.HandleStartJob(db))
downloadRoutes.POST("/jobs/:id/pause", middlewares.CheckAuth(db),controllers.HandlePauseJob())
downloadRoutes.POST("/jobs/:id/resume", middlewares.CheckAuth(db),controllers.HandleResumeJob(db))
downloadRoutes.DELETE("/jobs/:id", middlewares.CheckAuth(db),controllers.HandleDeleteJob(db))
}
}
func streamFile(c *gin.Context, filePath string) {
log.Printf("[STREAM] ➡️ Requête streaming sur %s", filePath)
f, err := os.Open(filePath)
if err != nil {
log.Printf("[STREAM][ERREUR] Impossible d'ouvrir: %v", err)
c.Status(http.StatusNotFound)
return
}
defer f.Close()
fileStat, err := f.Stat()
if err != nil {
log.Printf("[STREAM][ERREUR] Stat impossible: %v", err)
c.Status(http.StatusInternalServerError)
return
}
fileSize := fileStat.Size()
log.Printf("[STREAM] Taille du fichier: %d octets", fileSize)
c.Header("Accept-Ranges", "bytes")
c.Header("Content-Type", mimeTypeByExt(filePath))
rangeHeader := c.GetHeader("Range")
log.Printf("[STREAM] Range header reçu: %q", rangeHeader)
if rangeHeader == "" {
// Pas de Range, tout le fichier
c.Header("Content-Length", strconv.FormatInt(fileSize, 10))
log.Printf("[STREAM] 📦 Pas de Range, envoi du fichier complet (200 OK)")
http.ServeContent(c.Writer, c.Request, fileStat.Name(), fileStat.ModTime(), f)
return
}
// Analyse du Range
var start, end int64
start, end = 0, fileSize-1
if strings.HasPrefix(rangeHeader, "bytes=") {
rangeParts := strings.Split(strings.TrimPrefix(rangeHeader, "bytes="), "-")
if len(rangeParts) == 2 {
if s, err := strconv.ParseInt(rangeParts[0], 10, 64); err == nil {
start = s
}
if rangeParts[1] != "" {
if e, err := strconv.ParseInt(rangeParts[1], 10, 64); err == nil {
end = e
}
}
}
}
log.Printf("[STREAM] Parsed range: start=%d, end=%d", start, end)
if start > end || start < 0 || end >= fileSize {
log.Printf("[STREAM][ERREUR] Range invalide: %d-%d sur %d", start, end, fileSize)
c.Status(http.StatusRequestedRangeNotSatisfiable)
return
}
c.Status(http.StatusPartialContent)
c.Header("Content-Range", "bytes "+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(fileSize, 10))
c.Header("Content-Length", strconv.FormatInt(end-start+1, 10))
log.Printf("[STREAM] ▶️ Envoi bytes %d-%d sur %d [%s]", start, end, fileSize, filePath)
f.Seek(start, io.SeekStart)
n, err := io.CopyN(c.Writer, f, end-start+1)
if err != nil {
log.Printf("[STREAM][ERREUR] pendant l'envoi: %v", err)
} else {
log.Printf("[STREAM] ✔️ Fini, envoyé %d octets", n)
}
}
// Helper : extension → Content-Type (très basique)
func mimeTypeByExt(filePath string) string {
ext := strings.ToLower(filepath.Ext(filePath))
switch ext {
case ".mp4":
return "video/mp4"
case ".mkv":
return "video/x-matroska"
case ".webm":
return "video/webm"
case ".mp3":
return "audio/mpeg"
case ".pdf":
return "application/pdf"
default:
return "application/octet-stream"
}
}