diff --git a/bun.lockb b/bun.lockb index 02a3f78..3a84d96 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 01bfa33..6e5e8ac 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@mantine/hooks": "^7.16.1", "@mantine/modals": "^7.16.1", "next": "15.1.5", + "next-auth": "^4.24.11", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.4.0", diff --git a/src/app/admin/actions.ts b/src/app/admin/actions.ts deleted file mode 100644 index 0f9c94f..0000000 --- a/src/app/admin/actions.ts +++ /dev/null @@ -1,9 +0,0 @@ -"use server"; - -import { redirect } from "next/navigation"; - -export async function login(formData: { name: string; password: string }) { - console.log("data"); - console.log(formData); - redirect("/admin/panel"); -} diff --git a/src/app/admin/login/page.tsx b/src/app/admin/login/page.tsx new file mode 100644 index 0000000..6bbea86 --- /dev/null +++ b/src/app/admin/login/page.tsx @@ -0,0 +1,80 @@ +"use client"; +import { Button, Card, Flex, PasswordInput, Stack, Text, TextInput } from "@mantine/core"; +import { hasLength, useForm } from "@mantine/form"; +import { signIn } from "next-auth/react"; +import { redirect } from "next/navigation"; + +interface FormValues { + name: string; + password: string; +} + +export default function LoginPage() { + const form = useForm({ + mode: "uncontrolled", + initialValues: { + name: "", + password: "", + }, + validate: { + name: hasLength({ min: 5 }, "Too short"), + password: hasLength({ min: 5 }, "Too short"), + }, + }); + + const handleSubmit = async (formData: { name: string; password: string }) => { + const res = await signIn("credentials", { + username: formData.name, + password: formData.password, + redirect: false, + }); + if (res && res.error) { + if (res.status === 401) + form.setErrors({ + name: "Wrong password or username", + }); + else + form.setErrors({ + name: `Unknown error: ${res.status}`, + }); + } else { + redirect("/admin/panel"); + } + }; + + return ( + + +
+ + + Admin + + + + + +
+
+
+ ); +} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 4da4321..e0bf75a 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -1,60 +1,9 @@ -"use client"; +"use server"; -import { Button, Card, Flex, PasswordInput, Text, TextInput } from "@mantine/core"; -import { hasLength, useForm } from "@mantine/form"; -import { login } from "./actions"; - -interface FormValues { - name: string; - password: string; -} +import { redirect } from "next/navigation"; const AdminPage = () => { - const form = useForm({ - mode: "uncontrolled", - initialValues: { - name: "", - password: "", - }, - validate: { - name: hasLength({ min: 5 }, "Too short"), - password: hasLength({ min: 5 }, "Too short"), - }, - }); - - return ( - - -
- - Admin - - - - - -
-
- ); + return redirect("/admin/panel"); }; export default AdminPage; diff --git a/src/app/admin/layout.tsx b/src/app/admin/panel/layout.tsx similarity index 80% rename from src/app/admin/layout.tsx rename to src/app/admin/panel/layout.tsx index 70f87d1..d38236a 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/panel/layout.tsx @@ -1,9 +1,9 @@ "use client"; -import { ActionIcon, AppShell, Burger, Flex, Group, Skeleton, useMantineColorScheme } from "@mantine/core"; +import { ActionIcon, AppShell, Burger, Button, Flex, Group, Skeleton, useMantineColorScheme } from "@mantine/core"; import { useDisclosure } from "@mantine/hooks"; +import { signOut } from "next-auth/react"; import { LuMoon, LuSun } from "react-icons/lu"; - -const AdminLayout = ({ +const AdminPanelLayout = ({ children, }: Readonly<{ children: React.ReactNode; @@ -32,10 +32,13 @@ const AdminLayout = ({ .map((_, index) => ( ))} + {children} ); }; -export default AdminLayout; +export default AdminPanelLayout; diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..1540a6b --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,33 @@ +import NextAuth from "next-auth"; +import CredentialsProvider from "next-auth/providers/credentials"; + +const handler = NextAuth({ + jwt: { + maxAge: 60 * 60 * 24 * 30, + }, + session: { + strategy: "jwt", + }, + pages: { + signIn: "/admin/login", + }, + providers: [ + CredentialsProvider({ + name: "Credentials", + credentials: { + username: { label: "Username", type: "text", placeholder: "jsmith" }, + password: { label: "Password", type: "password" }, + }, + async authorize(credentials) { + const { username, password } = credentials as { username: string; password: string }; + if (username !== "admin" || password !== "admin") { + return null; + } + + return new Promise((resolve) => resolve({ id: "1", email: "example@example.org", name: "test" })); + }, + }), + ], +}); + +export { handler as GET, handler as POST }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 911df11..a5f5b97 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,8 +1,10 @@ import { ColorSchemeScript, MantineProvider } from "@mantine/core"; import "@mantine/core/styles.css"; import type { Metadata } from "next"; +import { getSession } from "next-auth/react"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.scss"; +import Providers from "./providers"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -19,18 +21,21 @@ export const metadata: Metadata = { description: "Generated by create next app", }; -export default function RootLayout({ +export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { + const session = await getSession(); return ( - {children} + + {children} + ); diff --git a/src/app/providers.tsx b/src/app/providers.tsx new file mode 100644 index 0000000..eca4ed9 --- /dev/null +++ b/src/app/providers.tsx @@ -0,0 +1,6 @@ +"use client"; +import type { Session } from "next-auth"; +import { SessionProvider } from "next-auth/react"; +export default function Providers({ session, children }: { session: Session | null; children: React.ReactNode }) { + return {children}; +} diff --git a/src/middleware.ts b/src/middleware.ts index e387e2f..781617d 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,14 +1,4 @@ -import { isAuthenticated } from "@/lib/auth"; -import { NextRequest, NextResponse } from "next/server"; - +export { default } from "next-auth/middleware"; export const config = { - matcher: "/admin/:path*", + matcher: ["/admin/panel"], }; - -export function middleware(request: NextRequest) { - const { pathname } = request.nextUrl; - if (pathname === "/admin" || isAuthenticated()) { - return NextResponse.next(); - } - return NextResponse.redirect(new URL("/admin", request.url)); -}