auth code changes / misc

This commit is contained in:
2025-11-25 05:56:46 -05:00
parent fb64a0f99a
commit 05aa48af14
7 changed files with 65 additions and 57 deletions

View File

@@ -2,6 +2,17 @@ import React, { useState, useRef, useEffect } from "react";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { API_URL } from "@/config"; import { API_URL } from "@/config";
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift());
return null;
}
function clearCookie(name) {
document.cookie = `${name}=; Max-Age=0; path=/;`;
}
export default function LoginPage() { export default function LoginPage() {
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
@@ -20,32 +31,24 @@ export default function LoginPage() {
setLoading(true); setLoading(true);
try { try {
if (!username) { if (!username || !password) {
setLoading(false); setLoading(false);
if (!toast.isActive("login-username-required-toast")) { if (!toast.isActive("login-missing-data-toast")) {
return toast.error("Username and password are required", toast.error("Username and password are required", {
{ toastId: "login-missing-data-toast",
toastId: "login-missing-data-toast", });
});
}
}
if (!password) {
setLoading(false);
if (!toast.isActive("login-password-required-toast")) {
return toast.error("Username and password are required",
{
toastId: "login-missing-data-toast",
});
} }
return;
} }
const formData = new URLSearchParams(); const formData = new URLSearchParams({
formData.append("username", username); username,
formData.append("password", password); password,
formData.append("grant_type", "password"); grant_type: "password",
formData.append("scope", ""); scope: "",
formData.append("client_id", ""); client_id: "",
formData.append("client_secret", ""); client_secret: "",
});
const resp = await fetch(`${API_URL}/auth/login`, { const resp = await fetch(`${API_URL}/auth/login`, {
method: "POST", method: "POST",
@@ -55,52 +58,40 @@ export default function LoginPage() {
}); });
if (resp.status === 401) { if (resp.status === 401) {
if (!toast.isActive("login-error-invalid-toast")) { toast.error("Invalid username or password", {
toast.error("Invalid username or password", { toastId: "login-error-invalid-toast",
toastId: "login-error-invalid-toast", });
});
}
setLoading(false); setLoading(false);
return; return;
} }
if (!resp.ok) { if (!resp.ok) {
const data = await resp.json().catch(() => ({})); const data = await resp.json().catch(() => ({}));
if (!toast.isActive("login-error-failed-toast")) { toast.error(data.detail ? `Login failed: ${data.detail}` : "Login failed", {
toast.error(data.detail ? `Login failed: ${data.detail}` : "Login failed", toastId: "login-error-failed-toast",
{ });
toastId: "login-error-failed-toast",
});
}
setLoading(false); setLoading(false);
return; return;
} }
const data = await resp.json(); const data = await resp.json();
if (data.access_token) { if (data.access_token) {
if (!toast.isActive("login-success-toast")) { toast.success("Login successful!", {
toast.success("Login successful!", toastId: "login-success-toast",
{ });
toastId: "login-success-toast", const returnTo = getCookie("returnTo") || "/TRip";
}); clearCookie("returnTo");
} window.location.href = returnTo;
window.location.href = "/TRip"; // TODO: fix, hardcoded
} else { } else {
if (!toast.isActive("login-error-no-token-toast")) { toast.error("Login failed: no access token received", {
toast.error("Login failed: no access token received", toastId: "login-error-no-token-toast",
{ });
toastId: "login-error-no-token-toast", setLoading(false);
});
setLoading(false);
}
} }
} catch (error) { } catch (error) {
if (!toast.isActive("login-error-network-toast")) { toast.error("Network error during login", {
toast.error("Network error during login", toastId: "login-error-network-toast",
{ });
toastId: "login-error-network-toast",
});
}
console.error("Login error:", error); console.error("Login error:", error);
setLoading(false); setLoading(false);
} }

View File

@@ -283,7 +283,9 @@ export default function MediaRequestForm() {
link.href = url; link.href = url;
const sanitize = (str) => str.replace(/[\\/:*?"<>|]/g, "_"); const sanitize = (str) => str.replace(/[\\/:*?"<>|]/g, "_");
const filename = `${sanitize(artist)} - ${sanitize(title)}.flac`; const urlPath = new URL(data.stream_url).pathname;
const extension = urlPath.split('.').pop().split('?')[0] || 'flac';
const filename = `${sanitize(artist)} - ${sanitize(title)}.${extension}`;
link.download = filename; link.download = filename;
document.body.appendChild(link); document.body.appendChild(link);

View File

@@ -3,6 +3,8 @@ import { metaData } from "../config";
import { Icon } from "astro-icon/components"; import { Icon } from "astro-icon/components";
import ExitToApp from '@mui/icons-material/ExitToApp'; import ExitToApp from '@mui/icons-material/ExitToApp';
const isLoggedIn = Astro.cookies.get('access_token') || Astro.cookies.get('refresh_token');
const navItems = [ const navItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
{ label: "Radio", href: "/radio" }, { label: "Radio", href: "/radio" },
@@ -11,6 +13,7 @@ const navItems = [
{ label: "TRip", href: "/TRip", auth: true }, { label: "TRip", href: "/TRip", auth: true },
{ label: "Status", href: "https://status.boatson.boats", icon: ExitToApp }, { label: "Status", href: "https://status.boatson.boats", icon: ExitToApp },
{ label: "Git", href: "https://kode.boatson.boats", icon: ExitToApp }, { label: "Git", href: "https://kode.boatson.boats", icon: ExitToApp },
// ...(isLoggedIn ? [{ label: "Logout", href: "#", onClick: "handleLogout()" }] : []), # todo
]; ];
const currentPath = Astro.url.pathname; const currentPath = Astro.url.pathname;

View File

@@ -3,10 +3,11 @@ import Base from "@/layouts/Base.astro";
import Root from "@/components/AppLayout.jsx"; import Root from "@/components/AppLayout.jsx";
import { requireAuthHook } from "@/hooks/requireAuthHook"; import { requireAuthHook } from "@/hooks/requireAuthHook";
const user = await requireAuthHook(Astro); const user = await requireAuthHook(Astro);
if (!user) { if (!user) {
const decodedUrl = decodeURIComponent(Astro.url.pathname + Astro.url.search);
Astro.cookies.set('returnTo', decodedUrl, { path: '/' });
return Astro.redirect('/login'); return Astro.redirect('/login');
} }

View File

@@ -6,6 +6,8 @@ import { requireAuthHook } from "@/hooks/requireAuthHook";
const user = await requireAuthHook(Astro); const user = await requireAuthHook(Astro);
if (!user) { if (!user) {
const decodedUrl = decodeURIComponent(Astro.url.pathname + Astro.url.search);
Astro.cookies.set('returnTo', decodedUrl, { path: '/' });
return Astro.redirect('/login'); return Astro.redirect('/login');
} }

View File

@@ -5,10 +5,11 @@ import { requireAuthHook } from "@/hooks/requireAuthHook";
const user = await requireAuthHook(Astro); const user = await requireAuthHook(Astro);
if (!user) { if (!user || !user.roles.includes('lighting')) {
const decodedUrl = decodeURIComponent(Astro.url.pathname + Astro.url.search);
Astro.cookies.set('returnTo', decodedUrl, { path: '/' });
return Astro.redirect('/login'); return Astro.redirect('/login');
} }
--- ---
<Base> <Base>

View File

@@ -50,3 +50,11 @@ export async function refreshAccessToken(cookieHeader) {
return null; return null;
} }
} }
export function handleLogout() {
document.cookie.split(";").forEach((cookie) => {
const name = cookie.split("=")[0].trim();
document.cookie = `${name}=; Max-Age=0; path=/;`;
});
window.location.href = "/";
}