Files
Puechberty Arthur b7010a1704 feat: add authentication and user management features
- Implemented AuthButton component for Discord sign-in and sign-out functionality.
- Created CopyButton component for copying server IP addresses.
- Developed EventCard and GradeCard components for displaying events and grades.
- Added Footer and Navbar components for site navigation and information.
- Introduced PurchaseButton for handling grade purchases with Stripe integration.
- Created SectionHeader component for consistent section titles.
- Implemented session management with SessionProvider for NextAuth.
- Set up PostgreSQL database with Docker and Prisma for data management.
- Added admin guard functionality to restrict access to certain routes.
- Configured NextAuth with Discord provider for user authentication.
- Defined Prisma schema for user, admin, grade, event, and purchase models.
- Seeded database with initial grades and events data.
- Added SVG hero image for the landing page.
- Extended NextAuth types to include additional user properties.
2026-04-28 21:09:55 +02:00

100 lines
2.8 KiB
TypeScript

import type { NextAuthOptions } from "next-auth";
import DiscordProvider from "next-auth/providers/discord";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { db } from "@/lib/db";
const adminDiscordId = process.env.ADMIN_DISCORD_ID;
const discordRedirectUrl = process.env.DISCORD_REDIRECT_URL;
const resolveAdmin = async (discordId?: string) => {
if (!discordId) return false;
if (adminDiscordId && discordId === adminDiscordId) return true;
const admin = await db.admin.findUnique({ where: { discordId } });
return Boolean(admin);
};
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(db),
session: {
strategy: "jwt",
},
providers: [
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID ?? "",
clientSecret: process.env.DISCORD_CLIENT_SECRET ?? "",
authorization: {
params: {
scope: "identify email",
...(discordRedirectUrl
? { redirect_uri: discordRedirectUrl }
: {}),
},
},
}),
],
callbacks: {
async jwt({ token, account, profile }) {
if (account && profile) {
const discordProfile = profile as {
id?: string;
username?: string;
avatar?: string;
};
token.discordId = discordProfile.id;
token.discordUsername = discordProfile.username;
token.discordAvatar = discordProfile.avatar;
}
token.isAdmin = await resolveAdmin(token.discordId as string | undefined);
return token;
},
async session({ session, token }) {
if (session.user) {
session.user.id = token.sub ?? "";
session.user.discordId = (token.discordId as string | undefined) ?? "";
session.user.discordUsername =
(token.discordUsername as string | undefined) ?? "";
session.user.discordAvatar =
(token.discordAvatar as string | undefined) ?? "";
session.user.isAdmin = token.isAdmin === true;
}
return session;
},
},
events: {
async signIn({ user, account, profile }) {
if (!account || account.provider !== "discord" || !profile) return;
const discordId = (profile as { id?: string }).id;
const discordUsername = (profile as { username?: string }).username;
const discordAvatar = (profile as { avatar?: string }).avatar;
if (!discordId) return;
await db.user.update({
where: { id: user.id },
data: {
discordId,
discordUsername,
discordAvatar,
},
});
if (adminDiscordId && discordId === adminDiscordId) {
await db.admin.upsert({
where: { discordId },
update: {},
create: {
discordId,
userId: user.id,
},
});
}
},
},
pages: {
signIn: "/auth/signin",
},
};