updates
Server Deploy / deploy (push) Successful in 2m16s

This commit is contained in:
domrichardson
2026-06-15 16:20:26 +01:00
parent e215ccc979
commit aaf154168e
14 changed files with 482 additions and 30 deletions
+9 -6
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import "./globals.css";
import { Providers } from "@/components/Providers";
import { AuthProvider } from "@/components/AuthProvider";
import { Sidebar } from "@/components/Sidebar";
export const metadata: Metadata = {
@@ -17,12 +18,14 @@ export default function RootLayout({
<html lang="en" className="dark">
<body className="bg-background text-text-primary">
<Providers>
<div className="flex h-screen overflow-hidden">
<Sidebar />
<main className="flex-1 overflow-y-auto">
{children}
</main>
</div>
<AuthProvider>
<div className="flex h-screen overflow-hidden">
<Sidebar />
<main className="flex-1 overflow-y-auto">
{children}
</main>
</div>
</AuthProvider>
</Providers>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
"use client";
import { createContext, useContext, useEffect, useState, ReactNode } from "react";
export interface User {
user_id: string;
email: string;
name: string;
}
interface AuthContextType {
user: User | null;
authEnabled: boolean;
}
const AuthContext = createContext<AuthContextType>({ user: null, authEnabled: false });
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [authEnabled, setAuthEnabled] = useState(false);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/auth/me", { credentials: "include" })
.then(async (res) => {
if (res.status === 401) {
window.location.href = "/auth/login";
return;
}
const data = await res.json();
if (data.auth_enabled === false) {
setAuthEnabled(false);
} else {
setAuthEnabled(true);
setUser(data as User);
}
setLoading(false);
})
.catch(() => {
// Backend unreachable — don't block the UI
setLoading(false);
});
}, []);
if (loading) {
return (
<div className="flex h-screen items-center justify-center bg-background">
<div className="h-8 w-8 animate-spin rounded-full border-2 border-border border-t-accent" />
</div>
);
}
return (
<AuthContext.Provider value={{ user, authEnabled }}>
{children}
</AuthContext.Provider>
);
}
+19 -1
View File
@@ -3,6 +3,7 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { clsx } from "clsx";
import { useAuth } from "@/components/AuthProvider";
interface NavItem {
href: string;
@@ -33,6 +34,7 @@ const navItems: NavItem[] = [
export function Sidebar() {
const pathname = usePathname();
const { user, authEnabled } = useAuth();
return (
<aside className="flex h-screen w-60 flex-col border-r border-border bg-surface">
@@ -71,7 +73,23 @@ export function Sidebar() {
</nav>
<div className="border-t border-border px-4 py-3">
<p className="text-xs text-text-secondary">KeyManager v1.0</p>
{authEnabled && user && (
<div className="mb-3">
<p className="truncate text-sm font-medium text-text-primary">{user.name || user.email}</p>
<p className="truncate text-xs text-text-secondary">{user.email}</p>
</div>
)}
<div className="flex items-center justify-between">
<p className="text-xs text-text-secondary">KeyManager v1.0</p>
{authEnabled && user && (
<a
href="/auth/logout"
className="text-xs text-text-secondary transition-colors hover:text-danger"
>
Logout
</a>
)}
</div>
</div>
</aside>
);
+1
View File
@@ -58,6 +58,7 @@ class ApiError extends Error {
async function request<T>(path: string, options?: RequestInit): Promise<T> {
const res = await fetch(`/api${path}`, {
credentials: "include",
headers: {
"Content-Type": "application/json",
...options?.headers,
+4
View File
@@ -10,6 +10,10 @@ const nextConfig: NextConfig = {
source: "/api/:path*",
destination: `${apiUrl}/api/:path*`,
},
{
source: "/auth/:path*",
destination: `${apiUrl}/auth/:path*`,
},
{
source: "/install",
destination: `${apiUrl}/install`,