diff --git a/internal/route/main.go b/internal/route/main.go index 19f559c..9221460 100644 --- a/internal/route/main.go +++ b/internal/route/main.go @@ -6,11 +6,13 @@ import ( "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" @@ -24,8 +26,23 @@ type spaHandler struct { } // Routes non protégées -func RoutesPublic(r *mux.Router, bd *gorm.DB) { +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( @@ -34,11 +51,11 @@ func RoutesPublic(r *mux.Router, bd *gorm.DB) { // Page de login r.HandleFunc("/login", renders.Login) - - // Endpoint d'API pour se logger 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" @@ -52,9 +69,7 @@ func RoutesPublic(r *mux.Router, bd *gorm.DB) { if info.IsDir() { return nil } - // On construit l'URL HTTP complète relPath, _ := filepath.Rel(uploadDir, path) - // Important : remplacer \ par / pour Windows relPath = filepath.ToSlash(relPath) fileURL := fmt.Sprintf("https://media.canguidev.fr/upload/%s", relPath) fmt.Fprintln(w, fileURL) @@ -65,13 +80,38 @@ func RoutesPublic(r *mux.Router, bd *gorm.DB) { http.Error(w, "Erreur lors de la génération de la playlist", http.StatusInternalServerError) } }) - webdavHandler := &webdav.Handler{ + + // 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.StripPrefix("/webdav/", webdavHandler)) + 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