- Users can now login via Bacanaks OR Piccadilly Discord server - Highest role from all servers is used (superadmin > moderator > user) - Lazy initialization fixes env loading timing issue - Updated documentation with implementation details and troubleshooting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
168 lines
4.3 KiB
JavaScript
168 lines
4.3 KiB
JavaScript
// Discord OAuth2 Service
|
|
const DISCORD_API = 'https://discord.com/api/v10';
|
|
|
|
// Lazy initialization - wird erst bei Verwendung geladen (nach dotenv)
|
|
let _guildConfigs = null;
|
|
|
|
function getGuildConfigs() {
|
|
if (_guildConfigs === null) {
|
|
_guildConfigs = [
|
|
{
|
|
name: 'Bacanaks',
|
|
guildId: process.env.DISCORD_GUILD_ID_1,
|
|
adminRoleId: process.env.DISCORD_ADMIN_ROLE_ID_1,
|
|
modRoleId: process.env.DISCORD_MOD_ROLE_ID_1
|
|
},
|
|
{
|
|
name: 'Piccadilly',
|
|
guildId: process.env.DISCORD_GUILD_ID_2,
|
|
adminRoleId: process.env.DISCORD_ADMIN_ROLE_ID_2,
|
|
modRoleId: process.env.DISCORD_MOD_ROLE_ID_2
|
|
}
|
|
].filter(config => config.guildId);
|
|
}
|
|
return _guildConfigs;
|
|
}
|
|
|
|
export function getDiscordAuthUrl() {
|
|
const params = new URLSearchParams({
|
|
client_id: process.env.DISCORD_CLIENT_ID,
|
|
redirect_uri: process.env.DISCORD_REDIRECT_URI,
|
|
response_type: 'code',
|
|
scope: 'identify guilds.members.read'
|
|
});
|
|
return `https://discord.com/oauth2/authorize?${params}`;
|
|
}
|
|
|
|
export async function exchangeCode(code) {
|
|
const response = await fetch(`${DISCORD_API}/oauth2/token`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
},
|
|
body: new URLSearchParams({
|
|
client_id: process.env.DISCORD_CLIENT_ID,
|
|
client_secret: process.env.DISCORD_CLIENT_SECRET,
|
|
grant_type: 'authorization_code',
|
|
code,
|
|
redirect_uri: process.env.DISCORD_REDIRECT_URI
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(`Failed to exchange code: ${error}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export async function getDiscordUser(accessToken) {
|
|
const response = await fetch(`${DISCORD_API}/users/@me`, {
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to get Discord user');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Prüft einen einzelnen Server
|
|
async function fetchGuildMember(guildId, userId) {
|
|
const response = await fetch(
|
|
`${DISCORD_API}/guilds/${guildId}/members/${userId}`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN}`
|
|
}
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
return null;
|
|
}
|
|
throw new Error(`Failed to get guild member from ${guildId}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Prüft alle konfigurierten Server und gibt Memberships zurück
|
|
export async function getGuildMemberships(userId) {
|
|
const configs = getGuildConfigs();
|
|
const memberships = [];
|
|
|
|
for (const config of configs) {
|
|
try {
|
|
const member = await fetchGuildMember(config.guildId, userId);
|
|
if (member) {
|
|
memberships.push({
|
|
config,
|
|
member,
|
|
roles: member.roles || []
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.error(`[Discord] Failed to check membership for guild ${config.name}:`, err.message);
|
|
}
|
|
}
|
|
|
|
return memberships.length > 0 ? memberships : null;
|
|
}
|
|
|
|
// Legacy-Funktion für Kompatibilität
|
|
export async function getGuildMember(userId) {
|
|
const memberships = await getGuildMemberships(userId);
|
|
if (!memberships || memberships.length === 0) {
|
|
return null;
|
|
}
|
|
return memberships[0].member;
|
|
}
|
|
|
|
// Rollen-Priorität: superadmin > moderator > user
|
|
const ROLE_PRIORITY = { superadmin: 3, moderator: 2, user: 1 };
|
|
|
|
// Bestimmt die höchste Rolle aus allen Server-Memberships
|
|
export function getUserRoleFromMemberships(memberships) {
|
|
if (!memberships || memberships.length === 0) {
|
|
return 'user';
|
|
}
|
|
|
|
let highestRole = 'user';
|
|
|
|
for (const { config, roles } of memberships) {
|
|
let role = 'user';
|
|
|
|
if (roles.includes(config.adminRoleId)) {
|
|
role = 'superadmin';
|
|
} else if (roles.includes(config.modRoleId)) {
|
|
role = 'moderator';
|
|
}
|
|
|
|
if (ROLE_PRIORITY[role] > ROLE_PRIORITY[highestRole]) {
|
|
highestRole = role;
|
|
}
|
|
}
|
|
|
|
return highestRole;
|
|
}
|
|
|
|
// Legacy-Funktion für Kompatibilität
|
|
export function getUserRole(memberRoles) {
|
|
const adminRoleId = process.env.DISCORD_ADMIN_ROLE_ID || process.env.DISCORD_ADMIN_ROLE_ID_1;
|
|
const modRoleId = process.env.DISCORD_MOD_ROLE_ID || process.env.DISCORD_MOD_ROLE_ID_1;
|
|
|
|
if (memberRoles.includes(adminRoleId)) {
|
|
return 'superadmin';
|
|
}
|
|
if (memberRoles.includes(modRoleId)) {
|
|
return 'moderator';
|
|
}
|
|
return 'user';
|
|
}
|