diff --git a/aethex-bot/server/webServer.js b/aethex-bot/server/webServer.js index 718f0d5..ae9279b 100644 --- a/aethex-bot/server/webServer.js +++ b/aethex-bot/server/webServer.js @@ -6,6 +6,7 @@ const path = require('path'); const crypto = require('crypto'); const Stripe = require('stripe'); const { invalidateCooldownCache } = require('../utils/cooldownManager'); +const { NexusClient } = require('../utils/nexusClient'); function createWebServer(discordClient, supabase, options = {}) { const app = express(); @@ -3126,6 +3127,160 @@ function createWebServer(discordClient, supabase, options = {}) { res.sendFile(path.join(__dirname, '../public/index.html')); }); + // ============================================ + // NEXUS Security API Integration Routes + // ============================================ + const nexusClient = new NexusClient(); + + // Middleware: Require AeThex session authentication + const requireAethexAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ error: 'AeThex authentication required. Please log in via Discord OAuth.' }); + } + next(); + }; + + // Middleware: Require admin permissions (MANAGE_GUILD permission) + const requireAdmin = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ error: 'Authentication required' }); + } + const guildId = req.query.guildId || req.body.guildId || req.params.guildId; + if (guildId) { + const guild = req.session.user.guilds?.find(g => g.id === guildId); + if (!guild || !guild.isAdmin) { + return res.status(403).json({ error: 'Admin permissions required for this guild' }); + } + } + next(); + }; + + // Authentication: Get token (requires AeThex session - for authenticated users only) + app.post('/api/nexus/token', requireAethexAuth, async (req, res) => { + try { + const result = await nexusClient.getToken(req.body); + res.json(result); + } catch (error) { + console.error('[NEXUS] Token error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Authentication: Refresh token (requires AeThex session) + app.post('/api/nexus/refresh', requireAethexAuth, async (req, res) => { + try { + const { refresh_token } = req.body; + if (!refresh_token) { + return res.status(400).json({ error: 'refresh_token required' }); + } + const result = await nexusClient.refreshToken(refresh_token); + res.json(result); + } catch (error) { + console.error('[NEXUS] Refresh error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Sentinel: Scrub text (requires AeThex session + NEXUS bearer token) + app.post('/api/nexus/scrub', requireAethexAuth, async (req, res) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'NEXUS Bearer token required' }); + } + const token = authHeader.split(' ')[1]; + + try { + const { text } = req.body; + if (!text) { + return res.status(400).json({ error: 'text required' }); + } + const result = await nexusClient.scrubText(text, token); + res.json(result); + } catch (error) { + console.error('[NEXUS] Scrub error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Sentinel: Check self IP (requires AeThex session + NEXUS bearer token) + app.get('/api/nexus/check-self', requireAethexAuth, async (req, res) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'NEXUS Bearer token required' }); + } + const token = authHeader.split(' ')[1]; + + try { + const result = await nexusClient.checkSelfIP(token); + res.json(result); + } catch (error) { + console.error('[NEXUS] Check-self error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Sentinel: Log activity (requires AeThex session + NEXUS bearer token) + app.post('/api/nexus/activity/log', requireAethexAuth, async (req, res) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'NEXUS Bearer token required' }); + } + const token = authHeader.split(' ')[1]; + + try { + const result = await nexusClient.logActivity(req.body, token); + res.json(result); + } catch (error) { + console.error('[NEXUS] Activity log error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Sentinel: Check IP blacklist (requires admin permissions + NEXUS bearer token) + app.get('/api/nexus/blacklist/check/:ip', requireAdmin, async (req, res) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'NEXUS Bearer token required' }); + } + const token = authHeader.split(' ')[1]; + + try { + const result = await nexusClient.checkIP(req.params.ip, token); + res.json(result); + } catch (error) { + console.error('[NEXUS] Blacklist check error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Sentinel: Get user snapshot (requires admin permissions + NEXUS bearer token) + app.get('/api/nexus/user-snapshot/:userId', requireAdmin, async (req, res) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'NEXUS Bearer token required' }); + } + const token = authHeader.split(' ')[1]; + + try { + const result = await nexusClient.getUserSnapshot(req.params.userId, token); + res.json(result); + } catch (error) { + console.error('[NEXUS] User snapshot error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + + // Public: Get NEXUS public stats (no auth required - public data only) + app.get('/api/nexus/public-stats', async (req, res) => { + try { + const result = await nexusClient.getPublicStats(); + res.json(result); + } catch (error) { + console.error('[NEXUS] Public stats error:', error.message); + res.status(400).json({ error: error.message }); + } + }); + // Catch-all route for SPA - use middleware instead of wildcard app.use((req, res, next) => { if (req.method === 'GET' && !req.path.startsWith('/api/')) {