package renders import ( "app/shelfly/internal/debridlink" "app/shelfly/internal/download" "app/shelfly/internal/models" "context" "encoding/json" "log" "net/http" "strconv" "text/template" "time" "gorm.io/gorm" ) func Login(w http.ResponseWriter, r *http.Request){ renderTemplate(w,"login",nil) } func Dashboard(db *gorm.DB)http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var paths []models.PathDownload if err := db.Find(&paths).Error; err != nil { http.Error(w, `{"error": "Failed to retrieve paths"}`, http.StatusInternalServerError) return } data := map[string]interface{}{ "paths": paths, } renderTemplate(w,"dashboard",data) } } func MenuLibrary(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var currentPaths []models.PathDownload if err := db.Find(¤tPaths).Error; err != nil { http.Error(w, `{"error": "Failed to retrieve paths"}`, http.StatusInternalServerError) return } // Récupérer l'ancienne version des paths (si existante) lastUpdate := r.Header.Get("HX-Current-Paths") var previousPaths []models.PathDownload if lastUpdate != "" { json.Unmarshal([]byte(lastUpdate), &previousPaths) } // Convertir en JSON pour comparaison currentJSON, _ := json.Marshal(currentPaths) previousJSON, _ := json.Marshal(previousPaths) // Vérifier si les paths ont changé pathsChanged := string(currentJSON) != string(previousJSON) data := map[string]interface{}{ "paths": currentPaths, } // Si HTMX request, ajouter les headers appropriés if r.Header.Get("HX-Request") == "true" { if pathsChanged { w.Header().Set("HX-Trigger", "pathsUpdated") } w.Header().Set("HX-Current-Paths", string(currentJSON)) } renderPartial(w, "dashboard", data) } } func Settings(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{ "Title": "Settings Page", "Options": []string{"Option 1", "Option 2", "Option 3"}, } renderPartial(w, "settings", data) } func Library(w http.ResponseWriter, r *http.Request) { renderPartial(w, "library",nil) } func GoDownload(w http.ResponseWriter, r *http.Request) { renderPartial(w, "godownloader_download",nil) } func GoDownloadLinkCollectors(w http.ResponseWriter, r *http.Request) { renderPartial(w, "godownloader_linkcollectors",nil) } func GetDebridClient(db *gorm.DB) *debridlink.Client { return debridlink.NewClient(db) } func GoDownloadSettingDelete(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := context.Background() DebridClient := GetDebridClient(db) idStr := r.URL.Query().Get("id") if idStr == "" { http.Error(w, "ID manquant", http.StatusBadRequest) return } idUint, err := strconv.ParseUint(idStr, 10, 64) if err != nil { http.Error(w, "ID invalide", http.StatusBadRequest) return } if err := DebridClient.DeleteDebridAccount(ctx, uint(idUint)); err != nil { http.Error(w, "Erreur lors de la suppression", http.StatusInternalServerError) return } http.Redirect(w, r, "/godownloader/settings", http.StatusSeeOther) } } func GoDownloadSettingToggleActive(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := context.Background() DebridClient := debridlink.NewClient(db) idStr := r.URL.Query().Get("id") idUint, err := strconv.ParseUint(idStr, 10, 32) if err != nil { http.Error(w, "ID invalide", http.StatusBadRequest) return } err = DebridClient.ToggleActiveStatus(ctx, uint(idUint)) if err != nil { log.Println("Erreur lors du toggle:", err) http.Error(w, "Échec de mise à jour", http.StatusInternalServerError) return } // Récupérer la liste mise à jour accounts, err := DebridClient.ListDebridAccounts(ctx) if err != nil { http.Error(w, "Erreur lors du chargement des comptes", http.StatusInternalServerError) return } // HTMX ou page normale if r.Header.Get("HX-Request") == "true" { renderPartial(w, "partials/accounts_table", map[string]interface{}{ "accounts": accounts, }) } else { renderPartial(w, "godownloader_setting", map[string]interface{}{ "accounts": accounts, }) } } } func GoDownloadSetting(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() client := debridlink.NewClient(db) switch r.Method { case http.MethodPost: if err := r.ParseForm(); err != nil { http.Error(w, "Form invalide", http.StatusBadRequest) return } username := r.FormValue("username") password := r.FormValue("password") deviceResp, err := client.RequestDeviceCodeWithCredentials(ctx, username, password) if err != nil { log.Println("[OAuth2] Erreur device_code:", err) http.Error(w, "Erreur OAuth: "+err.Error(), http.StatusInternalServerError) return } // Affiche le code + URL dans #auth-status renderPartial(w, "oauth_device_code", map[string]any{ "code": deviceResp.UserCode, "url": deviceResp.VerificationURL, }) // Polling async go func() { tokens, err := client.PollDeviceToken(context.Background(), deviceResp.DeviceCode, deviceResp.Interval) if err != nil { log.Println("[OAuth2] Polling échoué:", err) return } account := &debridlink.DebridAccount{ Host: "debrid-link.com", Username: username, Password: password, IsActive: true, AccessToken: tokens.AccessToken, RefreshToken: tokens.RefreshToken, ExpiresAt: time.Now().Add(time.Duration(tokens.ExpiresIn) * time.Second), } if err := db.Create(account).Error; err != nil { log.Println("[DB] Sauvegarde échouée:", err) return } log.Println("[OAuth2] Compte sauvegardé") }() case http.MethodGet: accounts, _ := client.ListDebridAccounts(ctx) renderPartial(w, "godownloader_setting", map[string]any{ "accounts": accounts, }) } } } func GoDownloadPartialTable(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() client := debridlink.NewClient(db) accounts, _ := client.ListDebridAccounts(ctx) renderPartial(w, "accounts_table", map[string]any{ "accounts": accounts, }) }} func PollStatusHandler(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var count int64 db.Model(&debridlink.DebridAccount{}).Where("is_active = ?", true).Count(&count) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]bool{ "success": count > 0, }) } } func GoDownload2(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { jobs := download.ListJobs() var paths []models.PathDownload db.Find(&paths) data := map[string]interface{}{ "jobs": jobs, "paths": paths, } renderTemplate(w, "godownloader_download", data) } } func HandleAddJob(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { r.ParseForm() link := r.FormValue("link") pathIDStr := r.FormValue("path_id") parsedID, err := strconv.Atoi(pathIDStr) if err != nil { http.Error(w, "Chemin invalide", http.StatusBadRequest) return } _ = download.AddJob(link, uint(parsedID), db) // Mise à jour de la vue partielle du tableau jobs := download.ListJobs() data := map[string]interface{}{ "jobs": jobs, } renderPartial(w, "downloads_table", data) } } func HandleListJobsPartial(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { jobs := download.ListJobs() data := map[string]interface{}{ "jobs": jobs, } renderPartial(w, "downloads_table", data) } } // func GoDownloadSetting(db *gorm.DB) http.HandlerFunc { // return func(w http.ResponseWriter, r *http.Request) { // ctx := r.Context() // // Initialise le client avec .env (client_id, secret) // DebridClient := debridlink.NewClient(db) // switch r.Method { // case http.MethodPost: // if err := r.ParseForm(); err != nil { // http.Error(w, "Formulaire invalide", http.StatusBadRequest) // return // } // host := r.FormValue("host") // username := r.FormValue("username") // password := r.FormValue("password") // isActive := r.FormValue("is_active") == "on" // // Authentification via Password Grant // tokens, err := DebridClient.PasswordGrant(ctx, username, password) // if err != nil { // log.Println("[OAuth2] Erreur:", err) // http.Error(w, "Authentification échouée", http.StatusUnauthorized) // return // } // // Création du compte à enregistrer // account := &debridlink.DebridAccount{ // Host: host, // Username: username, // Password: password, // IsActive: isActive, // AccessToken: tokens.AccessToken, // RefreshToken: tokens.RefreshToken, // ExpiresAt: time.Now().Add(time.Duration(tokens.ExpiresIn) * time.Second), // } // if err := db.Save(account).Error; err != nil { // log.Println("[DB] Sauvegarde échouée:", err) // http.Error(w, "Erreur DB", http.StatusInternalServerError) // return // } // var accounts []debridlink.DebridAccount // db.Order("id desc").Find(&accounts) // if r.Header.Get("HX-Request") == "true" { // renderPartial(w, "partials/accounts_table", map[string]interface{}{ // "accounts": accounts, // }) // return // } // renderPartial(w, "godownloader_setting", map[string]interface{}{ // "accounts": accounts, // }) // case http.MethodGet: // var accounts []debridlink.DebridAccount // db.Order("id desc").Find(&accounts) // renderPartial(w, "godownloader_setting", map[string]interface{}{ // "accounts": accounts, // }) // } // } // } func renderPartial(w http.ResponseWriter, templ string, data map[string]interface{}) { t, err := template.ParseFiles("./templates/" + templ + ".pages.tmpl") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } err = t.Execute(w, data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func renderTemplate(w http.ResponseWriter, templ string, data map[string]interface{}) { t, err := template.ParseFiles( "./templates/head.pages.tmpl", // Template inclus "./templates/" + templ + ".pages.tmpl", // Template principal ) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Exécutez explicitement le template principal err = t.ExecuteTemplate(w, templ+".pages.tmpl", data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }