diff --git a/aethex-bot/migrations/create_federation_tables.sql b/aethex-bot/migrations/create_federation_tables.sql new file mode 100644 index 0000000..cae533f --- /dev/null +++ b/aethex-bot/migrations/create_federation_tables.sql @@ -0,0 +1,128 @@ +-- Federation Tables for AeThex Bot +-- Run this in your Supabase SQL Editor + +-- Federation Servers - Tracks servers that are part of the federation +CREATE TABLE IF NOT EXISTS federation_servers ( + id SERIAL PRIMARY KEY, + guild_id VARCHAR(32) UNIQUE NOT NULL, + guild_name VARCHAR(255), + guild_icon VARCHAR(255), + owner_id VARCHAR(32), + member_count INTEGER DEFAULT 0, + status VARCHAR(32) DEFAULT 'pending', -- pending, approved, rejected, suspended + tier VARCHAR(32) DEFAULT 'free', -- free, premium + trust_level VARCHAR(32) DEFAULT 'bronze', -- bronze, silver, gold, platinum + reputation_score INTEGER DEFAULT 0, + subscription_id VARCHAR(255), + subscription_status VARCHAR(32), + invite_code VARCHAR(32), + description TEXT, + joined_federation_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Federation Bans - Shared ban list across federation servers +CREATE TABLE IF NOT EXISTS federation_bans ( + id SERIAL PRIMARY KEY, + user_id VARCHAR(32) NOT NULL, + username VARCHAR(255), + reason TEXT, + evidence TEXT, + severity VARCHAR(32) DEFAULT 'low', -- low, medium, high, critical + reported_by VARCHAR(32), + reported_by_guild_id VARCHAR(32), + active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Federation Applications - Servers applying to join +CREATE TABLE IF NOT EXISTS federation_applications ( + id SERIAL PRIMARY KEY, + guild_id VARCHAR(32) UNIQUE NOT NULL, + guild_name VARCHAR(255), + guild_icon VARCHAR(255), + owner_id VARCHAR(32), + member_count INTEGER DEFAULT 0, + reason TEXT, + status VARCHAR(32) DEFAULT 'pending', -- pending, approved, rejected + reviewed_by VARCHAR(32), + reviewed_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Federation Mappings - Cross-server role mappings +CREATE TABLE IF NOT EXISTS federation_mappings ( + id SERIAL PRIMARY KEY, + role_id VARCHAR(32) UNIQUE NOT NULL, + role_name VARCHAR(255), + guild_id VARCHAR(32) NOT NULL, + federation_role VARCHAR(64), -- The unified role name + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Federation Featured - Premium featured slots +CREATE TABLE IF NOT EXISTS federation_featured ( + id SERIAL PRIMARY KEY, + guild_id VARCHAR(32) UNIQUE NOT NULL, + subscription_id VARCHAR(255), + active BOOLEAN DEFAULT true, + expires_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Federation Reputation - Tracks server reputation metrics +CREATE TABLE IF NOT EXISTS federation_reputation ( + id SERIAL PRIMARY KEY, + guild_id VARCHAR(32) NOT NULL, + valid_reports INTEGER DEFAULT 0, + false_positives INTEGER DEFAULT 0, + total_reports INTEGER DEFAULT 0, + last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(guild_id) +); + +-- Create indexes for better query performance +CREATE INDEX IF NOT EXISTS idx_federation_servers_status ON federation_servers(status); +CREATE INDEX IF NOT EXISTS idx_federation_servers_guild_id ON federation_servers(guild_id); +CREATE INDEX IF NOT EXISTS idx_federation_bans_user_id ON federation_bans(user_id); +CREATE INDEX IF NOT EXISTS idx_federation_bans_active ON federation_bans(active); +CREATE INDEX IF NOT EXISTS idx_federation_applications_status ON federation_applications(status); +CREATE INDEX IF NOT EXISTS idx_federation_mappings_guild_id ON federation_mappings(guild_id); + +-- Disable RLS for now (or configure appropriate policies) +ALTER TABLE federation_servers ENABLE ROW LEVEL SECURITY; +ALTER TABLE federation_bans ENABLE ROW LEVEL SECURITY; +ALTER TABLE federation_applications ENABLE ROW LEVEL SECURITY; +ALTER TABLE federation_mappings ENABLE ROW LEVEL SECURITY; +ALTER TABLE federation_featured ENABLE ROW LEVEL SECURITY; +ALTER TABLE federation_reputation ENABLE ROW LEVEL SECURITY; + +-- Allow service role full access (for the bot) +CREATE POLICY "Service role has full access to federation_servers" ON federation_servers + FOR ALL USING (true) WITH CHECK (true); + +CREATE POLICY "Service role has full access to federation_bans" ON federation_bans + FOR ALL USING (true) WITH CHECK (true); + +CREATE POLICY "Service role has full access to federation_applications" ON federation_applications + FOR ALL USING (true) WITH CHECK (true); + +CREATE POLICY "Service role has full access to federation_mappings" ON federation_mappings + FOR ALL USING (true) WITH CHECK (true); + +CREATE POLICY "Service role has full access to federation_featured" ON federation_featured + FOR ALL USING (true) WITH CHECK (true); + +CREATE POLICY "Service role has full access to federation_reputation" ON federation_reputation + FOR ALL USING (true) WITH CHECK (true); + +-- Grant usage +GRANT ALL ON federation_servers TO service_role; +GRANT ALL ON federation_bans TO service_role; +GRANT ALL ON federation_applications TO service_role; +GRANT ALL ON federation_mappings TO service_role; +GRANT ALL ON federation_featured TO service_role; +GRANT ALL ON federation_reputation TO service_role; +GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO service_role; diff --git a/aethex-bot/server/webServer.js b/aethex-bot/server/webServer.js index ba17216..8b6dd9c 100644 --- a/aethex-bot/server/webServer.js +++ b/aethex-bot/server/webServer.js @@ -1320,17 +1320,22 @@ function createWebServer(discordClient, supabase, options = {}) { try { const [servers, bans, applications] = await Promise.all([ - supabase.from('federation_servers').select('id', { count: 'exact' }).eq('status', 'approved'), - supabase.from('federation_bans').select('id', { count: 'exact' }).eq('active', true), - supabase.from('federation_applications').select('id', { count: 'exact' }).eq('status', 'pending') + supabase.from('federation_servers').select('id', { count: 'exact', head: true }).eq('status', 'approved'), + supabase.from('federation_bans').select('id', { count: 'exact', head: true }).eq('active', true), + supabase.from('federation_applications').select('id', { count: 'exact', head: true }).eq('status', 'pending') ]); + console.log('[Federation Stats] servers:', servers); + console.log('[Federation Stats] bans:', bans); + console.log('[Federation Stats] applications:', applications); + res.json({ totalServers: servers.count || 0, activeBans: bans.count || 0, pendingApplications: applications.count || 0 }); } catch (error) { + console.error('[Federation Stats] Error:', error); res.status(500).json({ error: 'Failed to fetch stats' }); } }); @@ -1367,14 +1372,21 @@ function createWebServer(discordClient, supabase, options = {}) { } try { - const { data: servers } = await supabase + const { data: servers, error } = await supabase .from('federation_servers') .select('*') .eq('status', 'approved') .order('member_count', { ascending: false }); + console.log('[Federation Servers] Query result:', { servers, error }); + + if (error) { + console.error('[Federation Servers] Supabase error:', error); + } + res.json({ servers: servers || [] }); } catch (error) { + console.error('[Federation Servers] Error:', error); res.status(500).json({ error: 'Failed to fetch servers' }); } });