diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index 1e2f483..1e0efa4 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -153,6 +153,10 @@ model User { avatar String? isAdmin Boolean @default(false) isBanned Boolean @default(false) + inDiscord Boolean @default(false) + isVip Boolean @default(false) + isMod Boolean @default(false) + isStaff Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt comments Comment[] diff --git a/api/src/app/plugins/auth.ts b/api/src/app/plugins/auth.ts index 7b7689b..c3929bb 100644 --- a/api/src/app/plugins/auth.ts +++ b/api/src/app/plugins/auth.ts @@ -65,7 +65,7 @@ const authPlugin: FastifyPluginAsync = async (app) => { // Register Discord OAuth2 app.register(fastifyOauth2, { name: "oauth2Discord", - scope: ["identify", "email"], + scope: ["identify", "email", "guilds", "guilds.members.read"], credentials: { client: { id: process.env.DISCORD_CLIENT_ID || "", diff --git a/api/src/app/routes/auth/index.ts b/api/src/app/routes/auth/index.ts index 178721f..ed11000 100644 --- a/api/src/app/routes/auth/index.ts +++ b/api/src/app/routes/auth/index.ts @@ -28,8 +28,56 @@ const authRoutes: FastifyPluginAsync = async (app) => { const userData = await discordResponse.json(); + // Check if user is in our Discord server and has special roles + let inDiscord = false; + let isVip = false; + let isMod = false; + let isStaff = false; + const guildId = process.env.DISCORD_GUILD_ID; + const sponsorRoleId = process.env.SPONSOR_ROLE_ID; + const modRoleId = process.env.MOD_ROLE_ID; + const staffRoleId = process.env.STAFF_ROLE_ID; + + if (guildId) { + const guildsResponse = await fetch("https://discord.com/api/users/@me/guilds", { + headers: { + Authorization: `Bearer ${tokenResult.token.access_token}`, + }, + }); + + if (guildsResponse.ok) { + const guilds = await guildsResponse.json() as Array<{ id: string }>; + inDiscord = guilds.some(guild => guild.id === guildId); + } + + // If user is in Discord, check for special roles + if (inDiscord) { + const memberResponse = await fetch( + `https://discord.com/api/users/@me/guilds/${guildId}/member`, + { + headers: { + Authorization: `Bearer ${tokenResult.token.access_token}`, + }, + } + ); + + if (memberResponse.ok) { + const memberData = await memberResponse.json() as { roles: string[] }; + if (sponsorRoleId) { + isVip = memberData.roles.includes(sponsorRoleId); + } + if (modRoleId) { + isMod = memberData.roles.includes(modRoleId); + } + if (staffRoleId) { + isStaff = memberData.roles.includes(staffRoleId); + } + } + } + } + // Create or update user in database - const user = await authService.createOrUpdateUserFromDiscord(userData); + const user = await authService.createOrUpdateUserFromDiscord(userData, inDiscord, isVip, isMod, isStaff); // Generate JWT const jwt = await authService.generateToken(user); diff --git a/api/src/app/services/auth.service.ts b/api/src/app/services/auth.service.ts index fddeda8..2fc879e 100644 --- a/api/src/app/services/auth.service.ts +++ b/api/src/app/services/auth.service.ts @@ -50,13 +50,17 @@ export class AuthService { avatarUrl: dbUser.avatar || undefined, isAdmin: dbUser.isAdmin, isBanned: dbUser.isBanned, + inDiscord: dbUser.inDiscord, + isVip: dbUser.isVip, + isMod: dbUser.isMod, + isStaff: dbUser.isStaff, }; } /** * Create or update user from Discord OAuth data. */ - async createOrUpdateUserFromDiscord(discordData: DiscordUser): Promise { + async createOrUpdateUserFromDiscord(discordData: DiscordUser, inDiscord: boolean, isVip: boolean, isMod: boolean, isStaff: boolean): Promise { const avatarUrl = discordData.avatar ? `https://cdn.discordapp.com/avatars/${discordData.id}/${discordData.avatar}.png` : undefined; @@ -72,11 +76,19 @@ export class AuthService { email: discordData.email, avatar: avatarUrl, isAdmin: discordData.id === process.env.ADMIN_DISCORD_ID, + inDiscord, + isVip, + isMod, + isStaff, }, update: { username: discordData.username, email: discordData.email, avatar: avatarUrl, + inDiscord, + isVip, + isMod, + isStaff, }, }); @@ -88,6 +100,10 @@ export class AuthService { avatarUrl: dbUser.avatar || undefined, isAdmin: dbUser.isAdmin, isBanned: dbUser.isBanned, + inDiscord: dbUser.inDiscord, + isVip: dbUser.isVip, + isMod: dbUser.isMod, + isStaff: dbUser.isStaff, }; } } diff --git a/api/src/app/services/comment.service.ts b/api/src/app/services/comment.service.ts index 6ecb104..cc3f24d 100644 --- a/api/src/app/services/comment.service.ts +++ b/api/src/app/services/comment.service.ts @@ -60,6 +60,10 @@ export class CommentService { id: comment.user.id, username: comment.user.username, avatar: comment.user.avatar || undefined, + inDiscord: comment.user.inDiscord, + isVip: comment.user.isVip, + isMod: comment.user.isMod, + isStaff: comment.user.isStaff, }, gameId: comment.gameId || undefined, bookId: comment.bookId || undefined, diff --git a/api/src/app/services/user.service.ts b/api/src/app/services/user.service.ts index 3126a80..c07b57d 100644 --- a/api/src/app/services/user.service.ts +++ b/api/src/app/services/user.service.ts @@ -23,6 +23,10 @@ export class UserService { avatarUrl: user.avatar || undefined, isAdmin: user.isAdmin, isBanned: user.isBanned, + inDiscord: user.inDiscord, + isVip: user.isVip, + isMod: user.isMod, + isStaff: user.isStaff, })); } @@ -43,6 +47,10 @@ export class UserService { avatarUrl: user.avatar || undefined, isAdmin: user.isAdmin, isBanned: user.isBanned, + inDiscord: user.inDiscord, + isVip: user.isVip, + isMod: user.isMod, + isStaff: user.isStaff, }; } @@ -60,6 +68,10 @@ export class UserService { avatarUrl: user.avatar || undefined, isAdmin: user.isAdmin, isBanned: user.isBanned, + inDiscord: user.inDiscord, + isVip: user.isVip, + isMod: user.isMod, + isStaff: user.isStaff, }; } @@ -77,6 +89,10 @@ export class UserService { avatarUrl: user.avatar || undefined, isAdmin: user.isAdmin, isBanned: user.isBanned, + inDiscord: user.inDiscord, + isVip: user.isVip, + isMod: user.isMod, + isStaff: user.isStaff, }; } diff --git a/apps/frontend/src/app/components/admin/admin-users.component.ts b/apps/frontend/src/app/components/admin/admin-users.component.ts index ff7c30e..9529c6a 100644 --- a/apps/frontend/src/app/components/admin/admin-users.component.ts +++ b/apps/frontend/src/app/components/admin/admin-users.component.ts @@ -42,6 +42,18 @@ import { User } from '@library/shared-types'; @if (user.isBanned) { Banned } + @if (user.inDiscord) { + Discord + } + @if (user.isVip) { + VIP + } + @if (user.isMod) { + Mod + } + @if (user.isStaff) { + Staff + }