tweaked ui and updated invite command
This commit is contained in:
@@ -4,17 +4,68 @@ const cors = require('cors');
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3001;
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const corsOrigin = process.env.CORS_ORIGIN || null; // e.g. 'http://example.com' or '*' or 'http://127.0.0.1:3000'
|
||||
|
||||
app.use(cors());
|
||||
// Convenience base URLs (override if you want fully-qualified URLs)
|
||||
const BACKEND_BASE = process.env.BACKEND_BASE || `http://${host}:${port}`;
|
||||
const FRONTEND_BASE = process.env.FRONTEND_BASE || 'http://localhost:3000';
|
||||
|
||||
if (corsOrigin) {
|
||||
app.use(cors({ origin: corsOrigin }));
|
||||
} else {
|
||||
app.use(cors());
|
||||
}
|
||||
app.use(express.json());
|
||||
|
||||
const axios = require('axios');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Invite token helpers: short-lived HMAC-signed token so frontend can authorize invite deletes
|
||||
const INVITE_TOKEN_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
||||
const inviteTokenSecret = process.env.INVITE_TOKEN_SECRET || process.env.ENCRYPTION_KEY || 'fallback-invite-secret';
|
||||
|
||||
function generateInviteToken(guildId) {
|
||||
const payload = JSON.stringify({ gid: guildId, iat: Date.now() });
|
||||
const payloadB64 = Buffer.from(payload).toString('base64url');
|
||||
const h = crypto.createHmac('sha256', inviteTokenSecret).update(payloadB64).digest('base64url');
|
||||
return `${payloadB64}.${h}`;
|
||||
}
|
||||
|
||||
function verifyInviteToken(token) {
|
||||
try {
|
||||
if (!token) return null;
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 2) return null;
|
||||
const [payloadB64, sig] = parts;
|
||||
const expected = crypto.createHmac('sha256', inviteTokenSecret).update(payloadB64).digest('base64url');
|
||||
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) return null;
|
||||
const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf8'));
|
||||
if (!payload || !payload.gid || !payload.iat) return null;
|
||||
if (Date.now() - payload.iat > INVITE_TOKEN_TTL_MS) return null;
|
||||
return payload;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/auth/discord', (req, res) => {
|
||||
const url = `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&redirect_uri=${encodeURIComponent('http://localhost:3002/auth/discord/callback')}&response_type=code&scope=identify%20guilds`;
|
||||
const redirectUri = `${BACKEND_BASE}/auth/discord/callback`;
|
||||
const url = `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=identify%20guilds`;
|
||||
res.redirect(url);
|
||||
});
|
||||
|
||||
// Provide a short-lived invite token for frontend actions (delete). Not a replacement for proper auth
|
||||
app.get('/api/servers/:guildId/invite-token', (req, res) => {
|
||||
const { guildId } = req.params;
|
||||
try {
|
||||
const token = generateInviteToken(guildId);
|
||||
res.json({ token });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, message: 'Failed to generate token' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/auth/discord/callback', async (req, res) => {
|
||||
const code = req.query.code;
|
||||
if (!code) {
|
||||
@@ -27,7 +78,7 @@ app.get('/auth/discord/callback', async (req, res) => {
|
||||
params.append('client_secret', process.env.DISCORD_CLIENT_SECRET);
|
||||
params.append('grant_type', 'authorization_code');
|
||||
params.append('code', code);
|
||||
params.append('redirect_uri', 'http://localhost:3002/auth/discord/callback');
|
||||
params.append('redirect_uri', `${BACKEND_BASE}/auth/discord/callback`);
|
||||
|
||||
const response = await axios.post('https://discord.com/api/oauth2/token', params, {
|
||||
headers: {
|
||||
@@ -55,7 +106,7 @@ app.get('/auth/discord/callback', async (req, res) => {
|
||||
const db = readDb();
|
||||
user.theme = db.users && db.users[user.id] ? db.users[user.id].theme : 'light';
|
||||
const guilds = adminGuilds;
|
||||
res.redirect(`http://localhost:3000/dashboard?user=${encodeURIComponent(JSON.stringify(user))}&guilds=${encodeURIComponent(JSON.stringify(guilds))}`);
|
||||
res.redirect(`${FRONTEND_BASE}/dashboard?user=${encodeURIComponent(JSON.stringify(user))}&guilds=${encodeURIComponent(JSON.stringify(guilds))}`);
|
||||
} catch (error) {
|
||||
console.error('Error during Discord OAuth2 callback:', error);
|
||||
res.status(500).send('Internal Server Error');
|
||||
@@ -371,6 +422,12 @@ app.post('/api/servers/:guildId/invites', async (req, res) => {
|
||||
});
|
||||
|
||||
app.delete('/api/servers/:guildId/invites/:code', async (req, res) => {
|
||||
// Require a short-lived invite token issued by /api/servers/:guildId/invite-token
|
||||
const providedToken = req.headers['x-invite-token'];
|
||||
const payload = verifyInviteToken(providedToken);
|
||||
if (!payload || payload.gid !== req.params.guildId) {
|
||||
return res.status(401).json({ success: false, message: 'Unauthorized: missing or invalid invite token' });
|
||||
}
|
||||
try {
|
||||
const { guildId, code } = req.params;
|
||||
const db = readDb();
|
||||
@@ -404,6 +461,6 @@ const bot = require('../discord-bot');
|
||||
|
||||
bot.login();
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on port ${port}`);
|
||||
app.listen(port, host, () => {
|
||||
console.log(`Server is running on ${host}:${port}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user