feat: auth

This commit is contained in:
2026-02-04 08:04:46 -08:00
parent 8f3aeb9391
commit e167a17bd9
12 changed files with 673 additions and 9 deletions
+78
View File
@@ -0,0 +1,78 @@
import { FastifyInstance } from "fastify";
import { JwtPayload, User } from "@library/shared-types";
import { PrismaClient } from "../../generated/prisma";
export class AuthService {
private prisma: PrismaClient;
constructor(private readonly app: FastifyInstance) {
this.prisma = new PrismaClient();
}
/**
* Generate JWT token for user.
*/
async generateToken(user: User): Promise<string> {
const payload: JwtPayload = {
sub: user.id,
email: user.email,
username: user.username,
isAdmin: user.isAdmin,
};
return this.app.jwt.sign(payload, {
expiresIn: "7d",
});
}
/**
* Verify JWT token.
*/
async verifyToken(token: string): Promise<JwtPayload> {
return this.app.jwt.verify(token) as JwtPayload;
}
/**
* Create or update user from Discord OAuth data.
*/
async createOrUpdateUserFromDiscord(discordData: DiscordUser): Promise<User> {
const avatarUrl = discordData.avatar
? `https://cdn.discordapp.com/avatars/${discordData.id}/${discordData.avatar}.png`
: undefined;
// Upsert user in database
const dbUser = await this.prisma.user.upsert({
where: {
discordId: discordData.id,
},
create: {
discordId: discordData.id,
username: discordData.username,
email: discordData.email,
avatar: avatarUrl,
isAdmin: discordData.id === process.env.ADMIN_DISCORD_ID,
},
update: {
username: discordData.username,
email: discordData.email,
avatar: avatarUrl,
},
});
return {
id: dbUser.id,
discordId: dbUser.discordId,
username: dbUser.username,
email: dbUser.email,
avatarUrl: dbUser.avatar || undefined,
isAdmin: dbUser.isAdmin,
};
}
}
interface DiscordUser {
id: string;
username: string;
email: string;
avatar?: string;
}