Add multi-guild Discord OAuth support
- 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>
This commit is contained in:
@@ -49,13 +49,15 @@ Wenn der Bot einem Server beitritt, erstellt er automatisch folgende Struktur:
|
||||
|
||||
| Channel | @everyone | Bot |
|
||||
|---------|-----------|-----|
|
||||
| Kategorie | Lesen | Schreiben |
|
||||
| info | Lesen | Schreiben |
|
||||
| status | Lesen | Schreiben |
|
||||
| alerts | Lesen | Schreiben |
|
||||
| updates | Lesen | Schreiben |
|
||||
| diskussion | Lesen + Schreiben | Schreiben |
|
||||
| requests | Lesen + Threads erstellen | Schreiben |
|
||||
| Kategorie | Lesen | ViewChannel + Schreiben |
|
||||
| info | Lesen (kein Schreiben) | ViewChannel + Schreiben |
|
||||
| status | Lesen (kein Schreiben) | ViewChannel + Schreiben |
|
||||
| alerts | Lesen (kein Schreiben) | ViewChannel + Schreiben |
|
||||
| updates | Lesen (kein Schreiben) | ViewChannel + Schreiben |
|
||||
| diskussion | Lesen + Schreiben | ViewChannel + Schreiben |
|
||||
| requests | Lesen + Threads erstellen | ViewChannel + Schreiben |
|
||||
|
||||
**Wichtig**: Der Bot braucht explizit `ViewChannel` Permission für jeden Channel, auch wenn @everyone den Channel sehen kann. Ohne `ViewChannel` kann der Bot nicht in den Channel schreiben ("Missing Access" Fehler).
|
||||
|
||||
## Datenbank
|
||||
|
||||
@@ -80,6 +82,8 @@ CREATE TABLE guild_settings (
|
||||
);
|
||||
```
|
||||
|
||||
Die Datenbank liegt in `backend/db/users.sqlite`.
|
||||
|
||||
### DB-Funktionen
|
||||
|
||||
In `backend/db/init.js`:
|
||||
@@ -137,12 +141,43 @@ Jeder Server bekommt ein eigenes Embed mit:
|
||||
DISCORD_CLIENT_ID=1458251194806833306
|
||||
DISCORD_CLIENT_SECRET=xxx
|
||||
DISCORD_BOT_TOKEN=xxx
|
||||
DISCORD_GUILD_ID=729865854329815051 # Haupt-Server für Login
|
||||
DISCORD_ADMIN_ROLE_ID=1024693717434650736
|
||||
DISCORD_MOD_ROLE_ID=1024693170958766141
|
||||
|
||||
# Multi-Server OAuth Login
|
||||
# User muss nur in EINEM der Server Mitglied sein
|
||||
# Rollen werden pro Server geprüft, höchste Berechtigung zählt
|
||||
|
||||
# Server 1: Bacanaks
|
||||
DISCORD_GUILD_ID_1=729865854329815051
|
||||
DISCORD_ADMIN_ROLE_ID_1=1024693717434650736
|
||||
DISCORD_MOD_ROLE_ID_1=1024693170958766141
|
||||
|
||||
# Server 2: Piccadilly
|
||||
DISCORD_GUILD_ID_2=730907665802330224
|
||||
DISCORD_ADMIN_ROLE_ID_2=1458595551514988584
|
||||
DISCORD_MOD_ROLE_ID_2=1458591909210488914
|
||||
```
|
||||
|
||||
**Hinweis**: `DISCORD_GUILD_ID` wird nur für den Discord OAuth Login verwendet, nicht für den Bot selbst.
|
||||
**Hinweis**: Die Guild-IDs werden nur für den Discord OAuth Login verwendet, nicht für den Bot selbst.
|
||||
|
||||
### OAuth Login-Logik
|
||||
|
||||
```
|
||||
1. User loggt sich via Discord OAuth ein
|
||||
2. Für jeden konfigurierten Server:
|
||||
- Ist User Mitglied? → Rollen prüfen
|
||||
- Admin-Rolle → superadmin
|
||||
- Mod-Rolle → moderator
|
||||
- Nur Mitglied → user
|
||||
3. Höchste Berechtigung aus allen Servern wird verwendet
|
||||
4. Nicht in mindestens einem Server → Login verweigert
|
||||
```
|
||||
|
||||
| User ist in... | Bacanaks Rolle | Piccadilly Rolle | GSM Rolle |
|
||||
|----------------|----------------|------------------|-----------|
|
||||
| Nur Bacanaks | Admin | - | superadmin |
|
||||
| Nur Piccadilly | - | Mod | moderator |
|
||||
| Beide | Mitglied | Admin | superadmin |
|
||||
| Keinem | - | - | ❌ Kein Login |
|
||||
|
||||
### Developer Portal Einstellungen
|
||||
|
||||
@@ -158,12 +193,84 @@ DISCORD_MOD_ROLE_ID=1024693170958766141
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|--------------|
|
||||
| `backend/services/discord.js` | OAuth-Logik, Multi-Guild Membership-Prüfung |
|
||||
| `backend/services/discordBot.js` | Bot-Logik und Event-Handler |
|
||||
| `backend/routes/auth.js` | Auth-Endpoints (Login, Callback, Refresh) |
|
||||
| `backend/db/init.js` | Guild-Settings DB-Funktionen |
|
||||
| `frontend/src/pages/Dashboard.jsx` | Invite-Button im Dashboard |
|
||||
|
||||
## Implementierungsdetails
|
||||
|
||||
### Multi-Guild OAuth
|
||||
|
||||
Die OAuth-Implementierung in `discord.js` verwendet **lazy initialization** für die Guild-Konfigurationen:
|
||||
|
||||
```javascript
|
||||
let _guildConfigs = null;
|
||||
|
||||
function getGuildConfigs() {
|
||||
if (_guildConfigs === null) {
|
||||
_guildConfigs = [
|
||||
{ name: 'Bacanaks', guildId: process.env.DISCORD_GUILD_ID_1, ... },
|
||||
{ name: 'Piccadilly', guildId: process.env.DISCORD_GUILD_ID_2, ... }
|
||||
].filter(config => config.guildId);
|
||||
}
|
||||
return _guildConfigs;
|
||||
}
|
||||
```
|
||||
|
||||
**Wichtig**: Die Konfiguration darf NICHT beim Modul-Import initialisiert werden, da zu diesem Zeitpunkt dotenv die `.env` noch nicht geladen hat. Die lazy initialization stellt sicher, dass die Umgebungsvariablen verfügbar sind.
|
||||
|
||||
### Funktionen
|
||||
|
||||
| Funktion | Beschreibung |
|
||||
|----------|--------------|
|
||||
| `getGuildMemberships(userId)` | Prüft alle konfigurierten Server, gibt Array von Memberships zurück |
|
||||
| `getUserRoleFromMemberships(memberships)` | Bestimmt höchste Rolle aus allen Memberships |
|
||||
| `getGuildMember(userId)` | Legacy-Funktion, gibt ersten Match zurück |
|
||||
| `getUserRole(memberRoles)` | Legacy-Funktion für einzelne Rollen-Liste |
|
||||
|
||||
### Rollen-Priorität
|
||||
|
||||
```javascript
|
||||
const ROLE_PRIORITY = { superadmin: 3, moderator: 2, user: 1 };
|
||||
```
|
||||
|
||||
Bei mehreren Memberships wird immer die höchste Rolle verwendet.
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
- Der Bot muss auf **allen** konfigurierten Discord-Servern sein
|
||||
- Der Bot braucht Zugriff auf die Guild Members API
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Login schlägt fehl mit "nicht Mitglied"
|
||||
|
||||
1. **Bot auf allen Servern?** Der Bot muss auf Bacanaks UND Piccadilly eingeladen sein
|
||||
2. **Env-Variablen prüfen**:
|
||||
```bash
|
||||
grep GUILD /opt/gameserver-monitor/backend/.env
|
||||
```
|
||||
3. **Mit --update-env neustarten**:
|
||||
```bash
|
||||
pm2 restart gameserver-backend --update-env
|
||||
```
|
||||
4. **Logs prüfen**:
|
||||
```bash
|
||||
pm2 logs gameserver-backend --lines 30 | grep -i discord
|
||||
```
|
||||
|
||||
### Rolle wird nicht erkannt
|
||||
|
||||
1. **Rollen-IDs prüfen** - Discord Developer Mode aktivieren, Rechtsklick auf Rolle → ID kopieren
|
||||
2. **User-Rollen abfragen**:
|
||||
```bash
|
||||
curl -s -H "Authorization: Bot BOT_TOKEN" \
|
||||
"https://discord.com/api/v10/guilds/GUILD_ID/members/USER_ID" | jq '.roles'
|
||||
```
|
||||
3. **Konfigurierte IDs vergleichen** mit den tatsächlichen Rollen des Users
|
||||
|
||||
### Bot erstellt keine Channels
|
||||
|
||||
- Prüfen ob Bot "Manage Channels" Permission hat
|
||||
@@ -180,18 +287,52 @@ Suche nach `[DiscordBot]` Log-Einträgen.
|
||||
### Bot aus Datenbank entfernen
|
||||
|
||||
```bash
|
||||
sqlite3 /opt/gameserver-monitor/backend/users.sqlite
|
||||
cd /opt/gameserver-monitor/backend
|
||||
node -e "
|
||||
import Database from 'better-sqlite3';
|
||||
const db = new Database('./db/users.sqlite');
|
||||
db.prepare('DELETE FROM guild_settings WHERE guild_id = ?').run('GUILD_ID_HIER');
|
||||
console.log('Deleted');
|
||||
"
|
||||
```
|
||||
|
||||
Alternativ mit sqlite3 (falls installiert):
|
||||
```bash
|
||||
sqlite3 /opt/gameserver-monitor/backend/db/users.sqlite
|
||||
DELETE FROM guild_settings WHERE guild_id = 'xxx';
|
||||
```
|
||||
|
||||
### Missing Access Fehler
|
||||
|
||||
Wenn der Bot "Missing Access" meldet obwohl er eingeladen wurde:
|
||||
|
||||
1. **ViewChannel Permission prüfen**: Der Bot braucht explizit `ViewChannel` für jeden Channel
|
||||
2. Im Discord: Rechtsklick auf Channel → Bearbeiten → Berechtigungen → Bot auswählen → "Kanal ansehen" aktivieren
|
||||
3. Logs prüfen: `pm2 logs gameserver-backend --lines 50 | grep -i "missing\|access"`
|
||||
|
||||
### Status-Nachricht wurde gelöscht
|
||||
|
||||
Wenn die Status-Nachricht manuell gelöscht wurde, erscheint "Unknown Message" in den Logs. Fix:
|
||||
|
||||
```bash
|
||||
cd /opt/gameserver-monitor/backend
|
||||
node -e "
|
||||
import Database from 'better-sqlite3';
|
||||
const db = new Database('./db/users.sqlite');
|
||||
// Status Message ID auf NULL setzen, Bot erstellt neue beim nächsten Update
|
||||
db.prepare('UPDATE guild_settings SET status_message_id = NULL WHERE guild_id = ?').run('GUILD_ID_HIER');
|
||||
console.log('Reset status_message_id');
|
||||
"
|
||||
```
|
||||
|
||||
## Login vs. Bot
|
||||
|
||||
| Feature | Login (OAuth) | Bot |
|
||||
|---------|--------------|-----|
|
||||
| Erfordert Mitgliedschaft | Haupt-Discord | Nein |
|
||||
| Erfordert Mitgliedschaft | Bacanaks oder Piccadilly | Nein |
|
||||
| Server-Steuerung | Ja (je nach Rolle) | Nein |
|
||||
| Status sehen | Ja | Ja |
|
||||
| Alerts erhalten | Nein | Ja |
|
||||
| Verfügbar für | Haupt-Discord Mitglieder | Alle mit Bot |
|
||||
| Verfügbar für | Mitglieder beider Discord-Server | Alle mit Bot |
|
||||
|
||||
Der Login zur Webapp erfordert Mitgliedschaft im Haupt-Discord-Server (DISCORD_GUILD_ID). Der Bot ist davon unabhängig und zeigt nur passive Status-Updates.
|
||||
Der Login zur Webapp erfordert Mitgliedschaft in mindestens einem der konfigurierten Discord-Server (Bacanaks oder Piccadilly). Die höchste Rolle aus beiden Servern bestimmt die GSM-Berechtigung. Der Bot ist davon unabhängig und zeigt nur passive Status-Updates.
|
||||
|
||||
Reference in New Issue
Block a user