shelfy-v2/internal/routes/routes.go

204 lines
6.4 KiB
Go
Raw Normal View History

2025-07-27 14:26:30 +00:00
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)
2025-09-28 11:42:05 +00:00
2025-07-27 14:26:30 +00:00
}
}
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())
2025-09-28 12:04:59 +00:00
folderRoutes.DELETE("", middlewares.CheckAuth(db), controllers.DeleteHandler())
2025-09-28 11:42:05 +00:00
folderRoutes.DELETE("/", middlewares.CheckAuth(db), controllers.DeleteHandler())
folderRoutes.GET("/stats", middlewares.CheckAuth(db), controllers.FolderStatsHandler())
2025-07-27 14:26:30 +00:00
folderRoutes.GET("/file", middlewares.CheckAuth(db), func(c *gin.Context) {
path := c.Query("path")
filePath := filepath.Join("upload", filepath.Clean("/"+path))
2025-07-27 14:55:26 +00:00
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")
2025-07-27 14:26:30 +00:00
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"
}
}