- Fix issues with new setup

- Fix arr setup getting thr wrong crendentials
- Add file link invalidator
- Other minor bug fixes
This commit is contained in:
Mukhtar Akere
2025-10-08 08:13:13 +01:00
parent 22dae9efad
commit 700d00b802
29 changed files with 606 additions and 465 deletions
+9 -6
View File
@@ -12,16 +12,16 @@ import (
func (wb *Web) setupMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
needsAuth := cfg.NeedsSetup()
if needsAuth != nil && r.URL.Path != "/config" && r.URL.Path != "/api/config" {
http.Redirect(w, r, fmt.Sprintf("/config?inco=%s", needsAuth.Error()), http.StatusSeeOther)
needsSetup := cfg.CheckSetup()
if needsSetup != nil && r.URL.Path != "/settings" && r.URL.Path != "/api/config" {
http.Redirect(w, r, fmt.Sprintf("/settings?inco=%s", needsSetup.Error()), http.StatusSeeOther)
return
}
// strip inco from URL
if inco := r.URL.Query().Get("inco"); inco != "" && needsAuth == nil && r.URL.Path == "/config" {
if inco := r.URL.Query().Get("inco"); inco != "" && needsSetup == nil && r.URL.Path == "/settings" {
// redirect to the same URL without the inco parameter
http.Redirect(w, r, "/config", http.StatusSeeOther)
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}
next.ServeHTTP(w, r)
})
@@ -79,8 +79,11 @@ func (wb *Web) isAPIRequest(r *http.Request) bool {
func (wb *Web) sendJSONError(w http.ResponseWriter, message string, statusCode int) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
json.NewEncoder(w).Encode(map[string]interface{}{
err := json.NewEncoder(w).Encode(map[string]interface{}{
"error": message,
"status": statusCode,
})
if err != nil {
return
}
}
+21 -16
View File
@@ -1,53 +1,58 @@
package web
import (
"github.com/go-chi/chi/v5"
"io/fs"
"net/http"
"github.com/go-chi/chi/v5"
)
func (wb *Web) Routes() http.Handler {
r := chi.NewRouter()
// Load static files from embedded filesystem
staticFS, err := fs.Sub(assetsEmbed, "assets/build")
if err != nil {
panic(err)
}
imagesFS, err := fs.Sub(imagesEmbed, "assets/images")
if err != nil {
panic(err)
}
// Static assets - always public
staticFS, _ := fs.Sub(assetsEmbed, "assets/build")
imagesFS, _ := fs.Sub(imagesEmbed, "assets/images")
r.Handle("/assets/*", http.StripPrefix("/assets/", http.FileServer(http.FS(staticFS))))
r.Handle("/images/*", http.StripPrefix("/images/", http.FileServer(http.FS(imagesFS))))
// Public routes - no auth needed
r.Get("/version", wb.handleGetVersion)
r.Get("/login", wb.LoginHandler)
r.Post("/login", wb.LoginHandler)
r.Get("/register", wb.RegisterHandler)
r.Post("/register", wb.RegisterHandler)
r.Get("/skip-auth", wb.skipAuthHandler)
r.Get("/version", wb.handleGetVersion)
r.Post("/skip-auth", wb.skipAuthHandler)
// Protected routes - require auth
r.Group(func(r chi.Router) {
r.Use(wb.authMiddleware)
r.Use(wb.setupMiddleware)
// Web pages
r.Get("/", wb.IndexHandler)
r.Get("/download", wb.DownloadHandler)
r.Get("/repair", wb.RepairHandler)
r.Get("/stats", wb.StatsHandler)
r.Get("/config", wb.ConfigHandler)
r.Get("/settings", wb.ConfigHandler)
// API routes
r.Route("/api", func(r chi.Router) {
// Arr management
r.Get("/arrs", wb.handleGetArrs)
r.Post("/add", wb.handleAddContent)
// Repair operations
r.Post("/repair", wb.handleRepairMedia)
r.Get("/repair/jobs", wb.handleGetRepairJobs)
r.Post("/repair/jobs/{id}/process", wb.handleProcessRepairJob)
r.Post("/repair/jobs/{id}/stop", wb.handleStopRepairJob)
r.Delete("/repair/jobs", wb.handleDeleteRepairJob)
// Torrent management
r.Get("/torrents", wb.handleGetTorrents)
r.Delete("/torrents/{category}/{hash}", wb.handleDeleteTorrent)
r.Delete("/torrents/", wb.handleDeleteTorrents)
r.Delete("/torrents", wb.handleDeleteTorrents) // Fixed trailing slash
// Config/Auth
r.Get("/config", wb.handleGetConfig)
r.Post("/config", wb.handleUpdateConfig)
r.Post("/refresh-token", wb.handleRefreshAPIToken)
+10
View File
@@ -1,5 +1,15 @@
{{ define "config" }}
<div class="space-y-6">
{{ if .NeedSetup }}
<div role="alert" class="alert alert-warning">
<i class="bi bi-exclamation-triangle text-xl"></i>
<div>
<h3 class="font-bold">Configuration Required</h3>
<div class="text-sm">Your configuration is incomplete. Please complete the setup below.</div>
</div>
</div>
{{ end }}
<form id="configForm" class="space-y-6">
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
+10
View File
@@ -1,5 +1,15 @@
{{ define "download" }}
<div class="space-y-6">
{{ if .NeedSetup }}
<div role="alert" class="alert alert-warning">
<i class="bi bi-exclamation-triangle text-xl"></i>
<div>
<h3 class="font-bold">Configuration Required</h3>
<div class="text-sm">Your configuration is incomplete. Please complete the setup in the <a href="{{.URLBase}}settings" class="link link-hover font-semibold">Settings page</a>.</div>
</div>
</div>
{{ end }}
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<form id="downloadForm" enctype="multipart/form-data" class="space-y-3">
+10
View File
@@ -1,6 +1,16 @@
{{ define "index" }}
<div class="space-y-6">
{{ if .NeedSetup }}
<div role="alert" class="alert alert-warning">
<i class="bi bi-exclamation-triangle text-xl"></i>
<div>
<h3 class="font-bold">Configuration Required</h3>
<div class="text-sm">Your configuration is incomplete. Please complete the setup in the <a href="{{.URLBase}}settings" class="link link-hover font-semibold">Settings page</a>.</div>
</div>
</div>
{{ end }}
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
+2 -2
View File
@@ -54,7 +54,7 @@
<li><a href="{{.URLBase}}repair" class="{{if eq .Page "repair"}}active{{end}}">
<i class="bi bi-wrench-adjustable text-accent"></i>Repair
</a></li>
<li><a href="{{.URLBase}}config" class="{{if eq .Page "config"}}active{{end}}">
<li><a href="{{.URLBase}}settings" class="{{if eq .Page "config"}}active{{end}}">
<i class="bi bi-gear text-info"></i>Settings
</a></li>
<li><a href="{{.URLBase}}webdav" target="_blank">
@@ -85,7 +85,7 @@
<i class="bi bi-wrench-adjustable"></i>
<span class="hidden xl:inline">Repair</span>
</a></li>
<li><a href="{{.URLBase}}config" class="{{if eq .Page "config"}}active{{end}} tooltip tooltip-bottom" data-tip="Settings">
<li><a href="{{.URLBase}}settings" class="{{if eq .Page "config"}}active{{end}} tooltip tooltip-bottom" data-tip="Settings">
<i class="bi bi-gear"></i>
<span class="hidden xl:inline">Settings</span>
</a></li>
+1 -1
View File
@@ -75,7 +75,7 @@
// Handle skip auth button
skipAuthBtn.addEventListener('click', function() {
window.decypharrUtils.fetcher('/skip-auth', { method: 'GET' })
window.decypharrUtils.fetcher('/skip-auth', { method: 'POST' })
.then(response => {
if (response.ok) {
window.location.href = window.decypharrUtils.joinURL(window.urlBase, '/');
+10
View File
@@ -1,5 +1,15 @@
{{ define "repair" }}
<div class="space-y-6">
{{ if .NeedSetup }}
<div role="alert" class="alert alert-warning">
<i class="bi bi-exclamation-triangle text-xl"></i>
<div>
<h3 class="font-bold">Configuration Required</h3>
<div class="text-sm">Your configuration is incomplete. Please complete the setup in the <a href="{{.URLBase}}settings" class="link link-hover font-semibold">Settings page</a>.</div>
</div>
</div>
{{ end }}
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl mb-6">
+17 -9
View File
@@ -114,9 +114,11 @@ func (wb *Web) RegisterHandler(w http.ResponseWriter, r *http.Request) {
func (wb *Web) IndexHandler(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
data := map[string]interface{}{
"URLBase": cfg.URLBase,
"Page": "index",
"Title": "Torrents",
"URLBase": cfg.URLBase,
"Page": "index",
"Title": "Torrents",
"NeedSetup": cfg.CheckSetup() != nil,
"SetupError": cfg.CheckSetup(),
}
_ = wb.templates.ExecuteTemplate(w, "layout", data)
}
@@ -134,6 +136,8 @@ func (wb *Web) DownloadHandler(w http.ResponseWriter, r *http.Request) {
"Debrids": debrids,
"HasMultiDebrid": len(debrids) > 1,
"DownloadFolder": cfg.QBitTorrent.DownloadFolder,
"NeedSetup": cfg.CheckSetup() != nil,
"SetupError": cfg.CheckSetup(),
}
_ = wb.templates.ExecuteTemplate(w, "layout", data)
}
@@ -141,9 +145,11 @@ func (wb *Web) DownloadHandler(w http.ResponseWriter, r *http.Request) {
func (wb *Web) RepairHandler(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
data := map[string]interface{}{
"URLBase": cfg.URLBase,
"Page": "repair",
"Title": "Repair",
"URLBase": cfg.URLBase,
"Page": "repair",
"Title": "Repair",
"NeedSetup": cfg.CheckSetup() != nil,
"SetupError": cfg.CheckSetup(),
}
_ = wb.templates.ExecuteTemplate(w, "layout", data)
}
@@ -151,9 +157,11 @@ func (wb *Web) RepairHandler(w http.ResponseWriter, r *http.Request) {
func (wb *Web) ConfigHandler(w http.ResponseWriter, r *http.Request) {
cfg := config.Get()
data := map[string]interface{}{
"URLBase": cfg.URLBase,
"Page": "config",
"Title": "Config",
"URLBase": cfg.URLBase,
"Page": "config",
"Title": "Config",
"NeedSetup": cfg.CheckSetup() != nil,
"SetupError": cfg.CheckSetup(),
}
_ = wb.templates.ExecuteTemplate(w, "layout", data)
}