feat: add auth

This commit is contained in:
2025-12-25 22:32:04 +01:00
parent 71d915ff0f
commit e7fe00c48d
17 changed files with 1654 additions and 35 deletions

View File

@@ -1,4 +1,4 @@
import * as React from "react"
import * as React from "react";
import {
IconCamera,
IconChartBar,
@@ -15,12 +15,12 @@ import {
IconSearch,
IconSettings,
IconUsers,
} from "@tabler/icons-react"
} from "@tabler/icons-react";
import { NavDocuments } from "@/components/nav-documents"
import { NavMain } from "@/components/nav-main"
import { NavSecondary } from "@/components/nav-secondary"
import { NavUser } from "@/components/nav-user"
import { NavDocuments } from "@/components/nav-documents";
import { NavMain } from "@/components/nav-main";
import { NavSecondary } from "@/components/nav-secondary";
import { NavUser } from "@/components/nav-user";
import {
Sidebar,
SidebarContent,
@@ -29,14 +29,11 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar"
} from "@/components/ui/sidebar";
import { useQuery } from "@tanstack/react-query";
import type { User } from "@/models/user";
const data = {
user: {
name: "shadcn",
email: "m@example.com",
avatar: "/avatars/shadcn.jpg",
},
navMain: [
{
title: "Dashboard",
@@ -146,9 +143,18 @@ const data = {
icon: IconFileWord,
},
],
}
};
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const { data: user } = useQuery({
queryKey: ["user"],
queryFn: async () => {
const response = await fetch("/api/auth/user");
const user = (await response.json()) as User;
return user;
},
});
return (
<Sidebar collapsible="offcanvas" {...props}>
<SidebarHeader>
@@ -172,8 +178,14 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
<NavSecondary items={data.navSecondary} className="mt-auto" />
</SidebarContent>
<SidebarFooter>
<NavUser user={data.user} />
<NavUser
user={{
avatar: `https://i.pravatar.cc/200?u=${user?.id || ""}`,
email: user?.email || "",
name: user?.name || "",
}}
/>
</SidebarFooter>
</Sidebar>
)
);
}

View File

@@ -1,34 +1,76 @@
import { Separator } from "@/components/ui/separator";
import { SidebarTrigger } from "@/components/ui/sidebar";
import { ModeToggle } from "./mode-toggle";
import { useQuery } from "@tanstack/react-query";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Button } from "./ui/button";
import { useEffect, useState } from "react";
export function SiteHeader() {
const [oldLoginState, setOldLoginState] = useState(false);
const queryClient = useQueryClient();
const { data: pingResult } = useQuery({
queryKey: ["ping"],
queryFn: async () => {
let res = await fetch("/api/scp/ping");
return await res.text();
return (await res.text()) === "true";
},
refetchInterval: 30000,
});
const { data: isLoggedIn } = useQuery({
queryKey: ["is_scp_logged_in"],
queryFn: async () => {
let res = await fetch("/api/scp/auth/is_logged_in");
return (await res.text()) === "true";
},
refetchInterval: 5000,
});
useEffect(() => {
if (isLoggedIn) {
if (!oldLoginState) {
queryClient.invalidateQueries({ queryKey: ["user"] });
setOldLoginState(true);
}
}
}, [isLoggedIn]);
async function startFlow() {
let res = await fetch("/api/scp/auth/start_flow");
if (res.ok && window) {
let url = await res.text();
window.open(url, "_blank")?.focus();
}
}
return (
<header className="flex h-(--header-height) shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)">
<div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
<SidebarTrigger className="-ml-1" />
<div className="flex flex-row gap-2 justify-center items-center">
<span>Netcup</span>
{pingResult === "true" ? (
<div className="bg-green-500 rounded-[100%] animate-pulse w-4 h-4 ">
&nbsp;
</div>
{pingResult ? (
isLoggedIn ? (
<div className="bg-green-500 rounded-[100%] animate-pulse w-4 h-4 ">
&nbsp;
</div>
) : (
<div className="bg-yellow-500 rounded-[100%] animate-pulse w-4 h-4 ">
&nbsp;
</div>
)
) : (
<div className="bg-red-700 rounded-[100%] animate-pulse w-4 h-4">
&nbsp;
</div>
)}
</div>
{!isLoggedIn && pingResult && (
<Button variant="outline" size="sm" onClick={startFlow}>
Login to Netcup SCP
</Button>
)}
<Separator
orientation="vertical"
className="mx-2 data-[orientation=vertical]:h-4"

View File

@@ -0,0 +1,14 @@
export interface User {
autologinTokenUsed: string;
sub: string;
email_verified: boolean;
webservicePasswordUsed: string;
secureModeActive: string;
roles: string[];
name: string;
id: string;
preferred_username: string;
given_name: string;
family_name: string;
email: string;
}