working sessions

This commit is contained in:
2025-07-16 14:22:07 +02:00
parent 3bc2f7311b
commit 5e263ef1b8
11 changed files with 507 additions and 10 deletions
+45
View File
@@ -0,0 +1,45 @@
import axios, { HttpStatusCode } from "axios";
import { NextResponse } from "next/server";
import { findKey, sessions, users } from "../data";
export async function POST(request: Request) {
const body = await request.json();
if (!body.username || !body.password)
return NextResponse.json(
{ message: "Credentials are empty" },
{ status: HttpStatusCode.BadRequest }
);
const uuid = findKey(
users,
(u) => u.username == body.username && u.password == body.password
);
if (!uuid) {
return NextResponse.json(
{ message: "Invalid username or password" },
{ status: HttpStatusCode.BadRequest }
);
}
const sessionId = "SESH-" + crypto.randomUUID();
sessions[sessionId] = uuid;
return NextResponse.json({ message: "ok", sessionId });
}
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id") || "";
if (!sessions[id])
return NextResponse.json(
{ message: "Invalid session" },
{ status: HttpStatusCode.Unauthorized }
);
return NextResponse.json({ message: "ok" });
}
+22
View File
@@ -0,0 +1,22 @@
export function findKey<T extends object>(
obj: T,
predicate: (value: T[keyof T], key: keyof T) => boolean
): keyof T | undefined {
return Object.entries(obj).find(([key, value]) =>
predicate(value as T[keyof T], key as keyof T)
)?.[0] as keyof T | undefined;
}
export type User = {
username: string;
password: string;
};
export const users = {
admin: {
username: "admin",
password: "admin",
},
};
export const sessions: { [key: string]: string } = {};
+5
View File
@@ -0,0 +1,5 @@
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({ message: "ok", version: "0.0.1" });
}
+6
View File
@@ -0,0 +1,6 @@
import getSession from "@/hooks/getSession";
export default async function Dashboard() {
const session = await getSession();
}
+58 -5
View File
@@ -1,18 +1,71 @@
"use client";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import axios from "axios";
import { useState } from "react";
import Cookies from "js-cookie";
import { useRouter } from "next/navigation";
export default function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const router = useRouter();
const login = () => {
setError("");
axios({
method: "post",
url: "/api/auth",
data: {
username,
password,
},
})
.then((r) => {
Cookies.set("session_id", r.data.sessionId);
router.replace("/dashboard");
})
.catch((e) => {
setError(e.response.data.message);
});
};
return (
<div className="min-h-screen flex items-center justify-center">
<Card className="h-max w-[25rem] px-8 pb-10">
<Card className="h-max w-[25rem] px-8 pb-8 gap-3">
<h1 className="text-4xl font-bold text-center mt-5">Login</h1>
<Label>Username</Label>
<Input placeholder="Enter your username" />
<Label>Password</Label>
<Input placeholder="Enter your password" type="password" />
<Button>Login</Button>
<Input
placeholder="Enter your username"
onInput={(e) => {
setUsername((e.target as any).value);
}}
/>
<Label className="mt-1">Password</Label>
<Input
placeholder="Enter your password"
type="password"
onInput={(e) => {
setPassword((e.target as any).value);
}}
onKeyDown={(e) => {
if (e.key == "Enter" && username != "" && password != "") {
login();
}
}}
/>
{error ? <Label className="text-destructive">{error}</Label> : ""}
<Button
className="w-max mx-auto mt-3"
onClick={login}
disabled={username == "" || password == ""}
>
Login
</Button>
</Card>
</div>
);
+7 -3
View File
@@ -1,6 +1,10 @@
import getSession from "@/hooks/getSession";
import { redirect } from "next/navigation";
export default function Home() {
redirect("/login");
return <div>Hello, World!</div>;
export default async function Home() {
const session = await getSession();
if (session) {
redirect("/dashboard");
}
return <div>Redirecting..</div>;
}
+25
View File
@@ -0,0 +1,25 @@
import { cookies } from "next/headers";
import axios from "axios";
import Session from "@/lib/session";
import { redirect } from "next/navigation";
export default async function getSession(): Promise<Session | null> {
const cookieStore = await cookies();
const sessionId = cookieStore.get("session_id")?.value;
if (!sessionId) {
redirect("/login");
return null;
}
try {
await axios.get("http://localhost:3000/api/auth", {
params: { id: sessionId },
});
return new Session(sessionId);
} catch (err) {
redirect("/login");
return null;
}
}
+30
View File
@@ -0,0 +1,30 @@
import axios from "axios";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import Cookies from "js-cookie";
import Session from "@/lib/session";
export default function useSession() {
const router = useRouter();
const [session, setSession] = useState<Session | null>(null);
useEffect(() => {
const id = Cookies.get("session_id");
if (id)
axios({
method: "get",
url: "/api/auth",
params: { id },
})
.then((r) => {
setSession(new Session(id));
})
.catch((e) => {
console.log(e);
router.replace("/login");
});
});
return session;
}
+7
View File
@@ -0,0 +1,7 @@
export default class Session {
id: string;
constructor(id: string) {
this.id = id;
}
}