Fix: Correct ban migration logic

This commit is contained in:
gpt-engineer-app[bot]
2025-10-30 12:03:55 +00:00
parent e5de404e59
commit db101bc5f2
8 changed files with 509 additions and 52 deletions

View File

@@ -61,4 +61,7 @@ verify_jwt = true
verify_jwt = true
[functions.receive-inbound-email]
verify_jwt = false
[functions.process-expired-bans]
verify_jwt = false

View File

@@ -0,0 +1,107 @@
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4';
import { corsHeaders } from '../_shared/cors.ts';
Deno.serve(async (req) => {
// Handle CORS preflight
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
// Create admin client
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
{
auth: {
autoRefreshToken: false,
persistSession: false
}
}
);
const now = new Date().toISOString();
// Find expired bans
const { data: expiredBans, error: fetchError } = await supabaseAdmin
.from('profiles')
.select('user_id, username, ban_reason, ban_expires_at')
.eq('banned', true)
.not('ban_expires_at', 'is', null)
.lte('ban_expires_at', now);
if (fetchError) {
console.error('Error fetching expired bans:', fetchError);
throw fetchError;
}
console.log(`Found ${expiredBans?.length || 0} expired bans to process`);
// Unban users with expired bans
const unbannedUsers: string[] = [];
for (const profile of expiredBans || []) {
console.log(`Unbanning user: ${profile.username} (${profile.user_id})`);
const { error: unbanError } = await supabaseAdmin
.from('profiles')
.update({
banned: false,
ban_reason: null,
ban_expires_at: null
})
.eq('user_id', profile.user_id);
if (unbanError) {
console.error(`Failed to unban ${profile.username}:`, unbanError);
continue;
}
// Log the automatic unban
const { error: logError } = await supabaseAdmin
.rpc('log_admin_action', {
_admin_user_id: '00000000-0000-0000-0000-000000000000', // System user ID
_target_user_id: profile.user_id,
_action: 'auto_unban',
_details: {
reason: 'Ban expired',
original_ban_reason: profile.ban_reason,
expired_at: profile.ban_expires_at
}
});
if (logError) {
console.error(`Failed to log auto-unban for ${profile.username}:`, logError);
}
unbannedUsers.push(profile.username);
}
console.log(`Successfully unbanned ${unbannedUsers.length} users`);
return new Response(
JSON.stringify({
success: true,
unbanned_count: unbannedUsers.length,
unbanned_users: unbannedUsers,
processed_at: now
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 200
}
);
} catch (error) {
console.error('Error in process-expired-bans:', error);
return new Response(
JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error',
success: false
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 500
}
);
}
});

View File

@@ -0,0 +1,23 @@
-- Add ban expiration tracking
ALTER TABLE public.profiles
ADD COLUMN IF NOT EXISTS ban_expires_at timestamptz;
COMMENT ON COLUMN public.profiles.ban_expires_at IS
'When the ban expires (null = permanent ban). Automatic unbanning is handled by a scheduled edge function.';
-- Update existing banned users to have a default ban reason
UPDATE public.profiles
SET ban_reason = 'No reason provided (legacy ban)'
WHERE banned = true AND (ban_reason IS NULL OR ban_reason = '');
-- Add constraint to require ban_reason when banned
ALTER TABLE public.profiles
ADD CONSTRAINT ban_reason_required
CHECK (
(banned = true AND ban_reason IS NOT NULL AND ban_reason != '')
OR
(banned = false)
);
COMMENT ON CONSTRAINT ban_reason_required ON public.profiles IS
'Ensures that a ban reason must be provided when banning a user';