package route import ( "app/shelfly/internal/download" "app/shelfly/internal/library" "app/shelfly/internal/login" "app/shelfly/internal/users" "app/shelfly/renders" "encoding/base64" "fmt" "log" "net/http" "os" "path/filepath" "strings" "time" "github.com/gorilla/mux" "golang.org/x/net/webdav" "gorm.io/gorm" ) type spaHandler struct { staticPath string indexPath string } // Routes non protégées func checkAuth(authHeader, username, password string) bool { const prefix = "Basic " if !strings.HasPrefix(authHeader, prefix) { return false } decoded, err := base64.StdEncoding.DecodeString(authHeader[len(prefix):]) if err != nil { return false } pair := strings.SplitN(string(decoded), ":", 2) if len(pair) != 2 { return false } return pair[0] == username && pair[1] == password } func RoutesPublic(r *mux.Router, bd *gorm.DB) { // Fichiers statiques (CSS, JS, etc.) staticDir := "./templates/assets/" r.PathPrefix("/templates/assets/").Handler( http.StripPrefix("/templates/assets/", http.FileServer(http.Dir(staticDir))), ) // Page de login r.HandleFunc("/login", renders.Login) r.HandleFunc("/api/login", login.LoginHandler(bd)).Methods("POST") r.HandleFunc("/api/scan/{id}", library.ScanFolder(bd)).Methods("GET") r.HandleFunc("/api/download/stream", renders.HandleJobsStream(bd)) // Génération playlist r.HandleFunc("/playlist.m3u", func(w http.ResponseWriter, r *http.Request) { uploadDir := "/app/upload" w.Header().Set("Content-Type", "audio/x-mpegurl") fmt.Fprintln(w, "#EXTM3U") err := filepath.Walk(uploadDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } relPath, _ := filepath.Rel(uploadDir, path) relPath = filepath.ToSlash(relPath) fileURL := fmt.Sprintf("https://media.canguidev.fr/upload/%s", relPath) fmt.Fprintln(w, fileURL) return nil }) if err != nil { http.Error(w, "Erreur lors de la génération de la playlist", http.StatusInternalServerError) } }) // WebDAV sécurisé username := "tonuser" // ton login password := "tonpassword" // ton password webdavHandler := &webdav.Handler{ Prefix: "/webdav/", FileSystem: webdav.Dir("/app/upload"), LockSystem: webdav.NewMemLS(), } r.PathPrefix("/webdav/").Handler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { // Authentification auth := req.Header.Get("Authorization") if auth == "" || !checkAuth(auth, username, password) { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // Protection lecture seule if req.Method != "GET" && req.Method != "HEAD" && req.Method != "OPTIONS" && req.Method != "PROPFIND" { http.Error(w, "Read-Only", http.StatusForbidden) return } // Headers WebDAV que VLC attend w.Header().Set("DAV", "1,2") w.Header().Set("MS-Author-Via", "DAV") webdavHandler.ServeHTTP(w, req) })) } // Routes protégées func RoutesProtected(r *mux.Router, bd *gorm.DB) { // Ici on place les vues et API qui doivent être protégées r.HandleFunc("/stream", StreamHandler) r.HandleFunc("/dashboard", renders.Dashboard(bd)) r.HandleFunc("/settings", renders.Settings) r.HandleFunc("/library", renders.Library) r.HandleFunc("/menuLibary", renders.Library) r.HandleFunc("/godownloader/downloads", renders.GoDownload) r.HandleFunc("/godownloader/linkcollectors", renders.GoDownloadLinkCollectors) r.HandleFunc("/godownloader/settings", renders.GoDownloadSetting(bd)) r.HandleFunc("/godownloader/poll-status", renders.PollStatusHandler(bd)) r.HandleFunc("/godownloader/table-refresh", renders.GoDownloadPartialTable(bd)) r.HandleFunc("/godownloader/settings/delete", renders.GoDownloadSettingDelete(bd)) r.HandleFunc("/api/download/add", renders.HandleAddJob(bd)).Methods("POST") r.HandleFunc("/api/download/all", renders.HandleListJobsPartial(bd)).Methods("GET") r.HandleFunc("/downloads", renders.GoDownload2(bd)) r.HandleFunc("/stream/{id}", download.HandleStreamPage()).Methods("GET") r.HandleFunc("/api/download/start/{id}", renders.HandleStartJob(bd)).Methods("POST") r.HandleFunc("/api/download/pause/{id}", renders.HandlePauseJob).Methods("POST") r.HandleFunc("/api/download/resume/{id}", renders.HandleResumeJob(bd)).Methods("POST") r.HandleFunc("/api/download/delete/{id}", renders.HandleDeleteJob(bd)).Methods("DELETE") r.HandleFunc("/api/download/delete-multiple", renders.HandleDeleteMultipleJobs(bd)).Methods("POST") // API user r.HandleFunc("/api/user/create", users.CreateUser(bd)).Methods("POST") r.HandleFunc("/api/user/update/{id}", users.UpdateUser(bd)).Methods("PUT") r.HandleFunc("/api/user/delete/{id}", users.DeleteUser(bd)).Methods("DELETE") r.HandleFunc("/api/user/all/", users.ReadAllUser(bd)).Methods("GET") r.HandleFunc("/api/user/{id}", users.FindUserById(bd)).Methods("GET") // API download r.HandleFunc("/api/pathDownload/create", download.CreateSavePath(bd)).Methods("POST") r.HandleFunc("/api/pathDownload/update/{id}", download.UpdateSavePath(bd)).Methods("PUT") r.HandleFunc("/api/pathDownload/delete/{id}", download.DeleteSavePath(bd)).Methods("DELETE") r.HandleFunc("/api/pathDownload/all/", download.ReadAllSavePath(bd)).Methods("GET") //API Check path r.HandleFunc("/validate-path", download.PathValidationHandler) //API Scan folder } func StreamHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Le streaming n’est pas supporté par ce serveur", http.StatusInternalServerError) return } ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() // Boucle infinie (ou jusqu'à annulation) for { select { case <-ticker.C: fmt.Fprintf(w, "data:

Message #%d

\n\n") flusher.Flush() case <-r.Context().Done(): // Le client a probablement fermé la connexion log.Println("Client déconnecté") return } } }