2025-06-06 07:42:55 +00:00
|
|
|
package renders
|
|
|
|
|
|
|
|
|
|
import (
|
2025-06-09 14:13:32 +00:00
|
|
|
"app/shelfly/internal/debridlink"
|
2025-06-12 08:57:10 +00:00
|
|
|
"app/shelfly/internal/download"
|
2025-06-06 07:42:55 +00:00
|
|
|
"app/shelfly/internal/models"
|
2025-06-09 14:13:32 +00:00
|
|
|
"context"
|
2025-06-06 07:42:55 +00:00
|
|
|
"encoding/json"
|
2025-06-09 14:13:32 +00:00
|
|
|
"log"
|
2025-06-06 07:42:55 +00:00
|
|
|
"net/http"
|
2025-06-09 14:13:32 +00:00
|
|
|
"strconv"
|
2025-06-06 07:42:55 +00:00
|
|
|
"text/template"
|
2025-06-09 14:13:32 +00:00
|
|
|
"time"
|
2025-06-06 07:42:55 +00:00
|
|
|
|
|
|
|
|
"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)
|
|
|
|
|
}
|
2025-06-09 14:13:32 +00:00
|
|
|
func GetDebridClient(db *gorm.DB) *debridlink.Client {
|
|
|
|
|
return debridlink.NewClient(db)
|
2025-06-06 07:42:55 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
func GoDownloadSettingDelete(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
DebridClient := GetDebridClient(db)
|
2025-06-06 07:42:55 +00:00
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-12 08:57:10 +00:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-09 14:13:32 +00:00
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// }
|
2025-06-06 07:42:55 +00:00
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
// // 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")
|
2025-06-06 07:42:55 +00:00
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
err = t.Execute(w, data)
|
2025-06-06 07:42:55 +00:00
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
)
|
|
|
|
|
|
2025-06-06 07:42:55 +00:00
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
// Exécutez explicitement le template principal
|
|
|
|
|
err = t.ExecuteTemplate(w, templ+".pages.tmpl", data)
|
|
|
|
|
|
2025-06-06 07:42:55 +00:00
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-09 14:13:32 +00:00
|
|
|
|