diff --git a/frontend/assets/css/register-blocked.css b/frontend/assets/css/register-blocked.css new file mode 100644 index 0000000..3a117d2 --- /dev/null +++ b/frontend/assets/css/register-blocked.css @@ -0,0 +1,108 @@ +:root { + --bg: #111827; + --card: #1f2937; + --border: #374151; + --text: #f9fafb; + --text-muted: #9ca3af; + --accent: #3b82f6; + --accent-hover: #2563eb; + --success: #10b981; + --error: #ef4444; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + min-height: 100vh; + background: var(--bg); + color: var(--text); + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + display: flex; + flex-direction: column; +} + +body:not(.dashboard-layout) { + justify-content: center; + align-items: center; + padding: 1rem; +} + +.card { + width: 100%; + max-width: 400px; + background: var(--card); + border: 1px solid var(--border); + border-radius: 16px; + padding: 2.5rem 2rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -4px rgba(0, 0, 0, 0.3); + text-align: center; +} + +.card h1 { + margin: 0 0 0.5rem 0; + font-size: 1.75rem; + font-weight: 700; + letter-spacing: -0.025em; +} + +.card .subtitle { + color: var(--text-muted); + font-size: 0.95rem; + margin-bottom: 2rem; +} + +.btn { + display: inline-flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 0.85rem; + font-size: 1rem; + font-weight: 600; + border-radius: 10px; + border: none; + cursor: pointer; + text-decoration: none; + transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease; +} + +.btn:active { + transform: scale(0.98); +} + +.btn-secondary { + background: #1f2937; + color: white; + border: 1px solid var(--border); +} + +.btn-secondary:hover { + background: var(--accent); + border-color: var(--accent); +} + +.footer-text { + margin-top: 1.5rem; + font-size: 0.9rem; + color: var(--text-muted); +} + +.message { + margin-top: 1.25rem; + padding: 0.85rem 1rem; + border-radius: 10px; + font-size: 0.9rem; + line-height: 1.4; + text-align: left; +} + +.message.error { + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.2); + color: var(--error); + display: block; +} \ No newline at end of file diff --git a/frontend/assets/js/auth.js b/frontend/assets/js/auth.js index 5f43a5c..a844166 100644 --- a/frontend/assets/js/auth.js +++ b/frontend/assets/js/auth.js @@ -13,20 +13,15 @@ return null; } - const cookieAccessToken = getCookie("access_token"); - const cookieRefreshToken = getCookie("refresh_token"); - - const localAccessToken = localStorage.getItem("access_token"); - const localRefreshToken = localStorage.getItem("refresh_token"); - - const accessToken = cookieAccessToken || localAccessToken; - const refreshToken = cookieRefreshToken || localRefreshToken; - - if (!accessToken && !refreshToken) { - return; + function clearAllAuth() { + console.log("Clearing all auth remnants from cookies and localStorage..."); + localStorage.removeItem("access_token"); + localStorage.removeItem("refresh_token"); + document.cookie = "access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;"; + document.cookie = "refresh_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;"; } - async function tryTokenRefresh() { + async function tryTokenRefresh(refreshToken) { if (!refreshToken) return false; try { @@ -51,19 +46,28 @@ console.error("Refresh request failed:", err); } - document.cookie = "access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;"; - document.cookie = "refresh_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;"; return false; } async function checkAuth() { + const cookieAccessToken = getCookie("access_token"); + const cookieRefreshToken = getCookie("refresh_token"); + const localAccessToken = localStorage.getItem("access_token"); + const localRefreshToken = localStorage.getItem("refresh_token"); + + const accessToken = cookieAccessToken || localAccessToken; + const refreshToken = cookieRefreshToken || localRefreshToken; + console.log("Auth check started..."); console.log("AccessToken present:", !!accessToken); console.log("RefreshToken present:", !!refreshToken); - if (!cookieAccessToken && accessToken) { - console.log("Access token cookie missing, but present in localStorage. Forcing refresh..."); - } else if (accessToken) { + if (!accessToken && !refreshToken) { + console.log("No tokens found. User is guest."); + return; + } + + if (accessToken) { try { console.log("Attempting ping with access token..."); const response = await fetch("/api/ping", { @@ -76,7 +80,7 @@ window.location.href = "/dashboard"; return; } else { - console.log("Ping failed. Status:", response.status); + console.log("Ping failed. Token might be expired. Status:", response.status); } } catch (err) { console.error("Network error during ping:", err); @@ -85,24 +89,21 @@ if (refreshToken) { console.log("Starting token refresh to rebuild cookies..."); - const refreshSuccessful = await tryTokenRefresh(); + const refreshSuccessful = await tryTokenRefresh(refreshToken); if (refreshSuccessful) { console.log("Refresh successful! Redirecting to dashboard..."); window.location.href = "/dashboard"; return; } else { - console.log("Refresh failed. Staying on login."); + console.log("Refresh failed. Refresh token is invalid/expired."); } } else { - console.log("No refresh token present. User must log in normally."); + console.log("No refresh token present."); } - console.log("Authentication completely failed. Clearing remnants..."); - localStorage.removeItem("access_token"); - localStorage.removeItem("refresh_token"); - document.cookie = "access_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;"; - document.cookie = "refresh_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 UTC;"; + clearAllAuth(); + console.log("Authentication completely failed. Staying on current guest page."); } checkAuth(); diff --git a/frontend/handler.go b/frontend/handler.go index 0521f51..aecea49 100644 --- a/frontend/handler.go +++ b/frontend/handler.go @@ -3,8 +3,13 @@ package frontend import ( "html/template" "net/http" + "os" "path/filepath" "strings" + + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/css" + "github.com/tdewolff/minify/v2/js" ) var dashboard = template.Must(template.ParseFiles( @@ -116,7 +121,50 @@ func Projects(w http.ResponseWriter, r *http.Request) { } } +var minifier *minify.M + +func init() { + minifier = minify.New() + // Füge die Minifier für CSS und JS hinzu + minifier.AddFunc("text/css", css.Minify) + minifier.AddFunc("text/javascript", js.Minify) +} + func Assets(w http.ResponseWriter, r *http.Request) { path := strings.TrimPrefix(r.URL.Path, "/assets/") - http.ServeFile(w, r, filepath.Join("frontend/assets", path)) + fullPath := filepath.Join("frontend/assets", path) + + w.Header().Set("Cache-Control", "public, max-age=3600") + + var mimeType string + if strings.HasSuffix(path, ".min.js") { + mimeType = "text/javascript" + fullPath = strings.Replace(fullPath, ".min.js", ".js", 1) + } else if strings.HasSuffix(path, ".min.css") { + mimeType = "text/css" + fullPath = strings.Replace(fullPath, ".min.css", ".css", 1) + } + + info, err := os.Stat(fullPath) + if err != nil || info.IsDir() { + http.Error(w, "Asset not found", http.StatusNotFound) + return + } + + if mimeType != "" { + content, err := os.ReadFile(fullPath) + if err != nil { + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + minifiedContent, err := minifier.Bytes(mimeType, content) + if err == nil { + w.Header().Set("Content-Type", mimeType) + w.Write(minifiedContent) + return + } + } + + http.ServeFile(w, r, fullPath) } diff --git a/frontend/htmx/404.html b/frontend/htmx/404.html index 5507027..706579f 100644 --- a/frontend/htmx/404.html +++ b/frontend/htmx/404.html @@ -4,8 +4,8 @@