Add ban reason to profiles

This commit is contained in:
gpt-engineer-app[bot]
2025-10-30 02:51:16 +00:00
parent e0d1a66fb2
commit e5de404e59
8 changed files with 146 additions and 10 deletions

View File

@@ -82,6 +82,31 @@ export function AuthModal({ open, onOpenChange, defaultTab = 'signin' }: AuthMod
const { data, error } = await supabase.auth.signInWithPassword(signInOptions);
if (error) throw error;
// CRITICAL: Check ban status immediately after successful authentication
const { data: profile } = await supabase
.from('profiles')
.select('banned, ban_reason')
.eq('user_id', data.user.id)
.single();
if (profile?.banned) {
// Sign out immediately
await supabase.auth.signOut();
const reason = profile.ban_reason
? `Reason: ${profile.ban_reason}`
: 'Contact support for assistance.';
toast({
variant: "destructive",
title: "Account Suspended",
description: `Your account has been suspended. ${reason}`,
duration: 10000
});
setLoading(false);
return; // Stop authentication flow
}
// Check if MFA is required (user exists but no session)
if (data.user && !data.session) {
const totpFactor = data.user.factors?.find(f => f.factor_type === 'totp' && f.status === 'verified');

View File

@@ -8,6 +8,7 @@ export function useBanCheck() {
const { user } = useAuth();
const navigate = useNavigate();
const [isBanned, setIsBanned] = useState(false);
const [banReason, setBanReason] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
@@ -21,15 +22,21 @@ export function useBanCheck() {
try {
const { data: profile } = await supabase
.from('profiles')
.select('banned')
.select('banned, ban_reason')
.eq('user_id', user.id)
.single();
if (profile?.banned) {
setIsBanned(true);
setBanReason(profile.ban_reason || null);
const reason = profile.ban_reason
? `Reason: ${profile.ban_reason}`
: 'Contact support for assistance.';
toast({
title: 'Account Suspended',
description: 'Your account has been suspended. Contact support for assistance.',
description: `Your account has been suspended. ${reason}`,
variant: 'destructive',
duration: Infinity // Don't auto-dismiss
});
@@ -58,14 +65,20 @@ export function useBanCheck() {
filter: `user_id=eq.${user.id}`
},
(payload) => {
const newProfile = payload.new as { banned: boolean };
const newProfile = payload.new as { banned: boolean; ban_reason: string | null };
// Handle BAN event
if (newProfile.banned && !isBanned) {
setIsBanned(true);
setBanReason(newProfile.ban_reason || null);
const reason = newProfile.ban_reason
? `Reason: ${newProfile.ban_reason}`
: 'Contact support for assistance.';
toast({
title: 'Account Suspended',
description: 'Your account has been suspended. Contact support for assistance.',
description: `Your account has been suspended. ${reason}`,
variant: 'destructive',
duration: Infinity
});
@@ -76,6 +89,7 @@ export function useBanCheck() {
// Handle UNBAN event
if (!newProfile.banned && isBanned) {
setIsBanned(false);
setBanReason(null);
toast({
title: 'Account Restored',
description: 'Your account has been unbanned. You can now use the application normally.',
@@ -92,5 +106,5 @@ export function useBanCheck() {
};
}, [user, navigate]);
return { isBanned, loading };
return { isBanned, loading, banReason };
}

View File

@@ -1952,6 +1952,7 @@ export type Database = {
Row: {
avatar_image_id: string | null
avatar_url: string | null
ban_reason: string | null
banned: boolean
bio: string | null
coaster_count: number | null
@@ -1983,6 +1984,7 @@ export type Database = {
Insert: {
avatar_image_id?: string | null
avatar_url?: string | null
ban_reason?: string | null
banned?: boolean
bio?: string | null
coaster_count?: number | null
@@ -2014,6 +2016,7 @@ export type Database = {
Update: {
avatar_image_id?: string | null
avatar_url?: string | null
ban_reason?: string | null
banned?: boolean
bio?: string | null
coaster_count?: number | null

View File

@@ -104,6 +104,31 @@ export default function Auth() {
if (error) throw error;
// CRITICAL: Check ban status immediately after successful authentication
const { data: profile } = await supabase
.from('profiles')
.select('banned, ban_reason')
.eq('user_id', data.user.id)
.single();
if (profile?.banned) {
// Sign out immediately
await supabase.auth.signOut();
const reason = profile.ban_reason
? `Reason: ${profile.ban_reason}`
: 'Contact support for assistance.';
toast({
variant: "destructive",
title: "Account Suspended",
description: `Your account has been suspended. ${reason}`,
duration: 10000
});
setLoading(false);
return; // Stop authentication flow
}
// Check if MFA is required (user exists but no session)
if (data.user && !data.session) {
const totpFactor = data.user.factors?.find(f => f.factor_type === 'totp' && f.status === 'verified');

View File

@@ -51,6 +51,31 @@ export default function AuthCallback() {
const user = session.user;
// CRITICAL: Check ban status immediately after getting session
const { data: banProfile } = await supabase
.from('profiles')
.select('banned, ban_reason')
.eq('user_id', user.id)
.single();
if (banProfile?.banned) {
await supabase.auth.signOut();
const reason = banProfile.ban_reason
? `Reason: ${banProfile.ban_reason}`
: 'Contact support for assistance.';
toast({
variant: 'destructive',
title: 'Account Suspended',
description: `Your account has been suspended. ${reason}`,
duration: 10000
});
navigate('/auth');
return; // Stop OAuth processing
}
// Check if this is a new OAuth user (created within last minute)
const createdAt = new Date(user.created_at);
const now = new Date();