Fix: Add dashboard widget for flow violations

This commit is contained in:
gpt-engineer-app[bot]
2025-10-06 17:01:41 +00:00
parent 124fe5e76e
commit 3210147654
3 changed files with 117 additions and 2 deletions

View File

@@ -155,6 +155,11 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
const { preferences } = useUnitPreferences();
const measurementSystem = preferences.measurement_system;
// Validate that onSubmit uses submission helpers (dev mode only)
useEffect(() => {
validateSubmissionHandler(onSubmit, 'ride');
}, [onSubmit]);
// Manufacturer and model state
const [selectedManufacturerId, setSelectedManufacturerId] = useState<string>(
initialData?.manufacturer_id || ''

View File

@@ -1,6 +1,6 @@
import { useRef, useEffect, useCallback, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { FileText, Flag, AlertCircle, Activity } from 'lucide-react';
import { FileText, Flag, AlertCircle, Activity, ShieldAlert } from 'lucide-react';
import { useUserRole } from '@/hooks/useUserRole';
import { useAuth } from '@/hooks/useAuth';
import { Card, CardContent } from '@/components/ui/card';
@@ -12,6 +12,8 @@ import { ReportsQueue } from '@/components/moderation/ReportsQueue';
import { RecentActivity } from '@/components/moderation/RecentActivity';
import { useModerationStats } from '@/hooks/useModerationStats';
import { useAdminSettings } from '@/hooks/useAdminSettings';
import { supabase } from '@/integrations/supabase/client';
import { Alert, AlertDescription } from '@/components/ui/alert';
export default function AdminDashboard() {
const { user, loading: authLoading } = useAuth();
@@ -19,6 +21,7 @@ export default function AdminDashboard() {
const navigate = useNavigate();
const [isRefreshing, setIsRefreshing] = useState(false);
const [activeTab, setActiveTab] = useState('moderation');
const [suspiciousVersionsCount, setSuspiciousVersionsCount] = useState<number>(0);
const moderationQueueRef = useRef<ModerationQueueRef>(null);
const reportsQueueRef = useRef<any>(null);
@@ -38,9 +41,28 @@ export default function AdminDashboard() {
pollingInterval: pollInterval,
});
// Check for suspicious versions (bypassed submission flow)
const checkSuspiciousVersions = useCallback(async () => {
if (!user || !isModerator()) return;
const { count, error } = await supabase
.from('entity_versions')
.select('*', { count: 'exact', head: true })
.is('changed_by', null);
if (!error && count !== null) {
setSuspiciousVersionsCount(count);
}
}, [user, isModerator]);
useEffect(() => {
checkSuspiciousVersions();
}, [checkSuspiciousVersions]);
const handleRefresh = useCallback(async () => {
setIsRefreshing(true);
await refreshStats();
await checkSuspiciousVersions();
// Refresh active tab's content
switch (activeTab) {
@@ -56,7 +78,7 @@ export default function AdminDashboard() {
}
setTimeout(() => setIsRefreshing(false), 500);
}, [refreshStats, activeTab]);
}, [refreshStats, checkSuspiciousVersions, activeTab]);
const handleStatCardClick = (cardType: 'submissions' | 'reports' | 'flagged') => {
switch (cardType) {
@@ -143,6 +165,17 @@ export default function AdminDashboard() {
</p>
</div>
{/* Security Warning for Suspicious Versions */}
{suspiciousVersionsCount > 0 && (
<Alert variant="destructive" className="border-red-500/50 bg-red-500/10">
<ShieldAlert className="h-5 w-5" />
<AlertDescription className="ml-2">
<strong>Security Alert:</strong> {suspiciousVersionsCount} entity version{suspiciousVersionsCount !== 1 ? 's' : ''} detected without user attribution.
This may indicate submission flow bypass. Check admin audit logs for details.
</AlertDescription>
</Alert>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{statCards.map((card) => {
const Icon = card.icon;

View File

@@ -0,0 +1,77 @@
-- Update auto_create_entity_version to log suspicious versions without user attribution
CREATE OR REPLACE FUNCTION public.auto_create_entity_version()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
v_entity_type TEXT;
v_change_type version_change_type;
v_user_id UUID;
v_version_data JSONB;
BEGIN
-- Determine entity type from table name
v_entity_type := CASE TG_TABLE_NAME
WHEN 'parks' THEN 'park'
WHEN 'rides' THEN 'ride'
WHEN 'companies' THEN 'company'
WHEN 'ride_models' THEN 'ride_model'
WHEN 'photos' THEN 'photo'
ELSE substring(TG_TABLE_NAME from 1 for length(TG_TABLE_NAME) - 1)
END;
-- Determine change type
v_change_type := CASE TG_OP
WHEN 'INSERT' THEN 'created'::version_change_type
WHEN 'UPDATE' THEN 'updated'::version_change_type
ELSE 'updated'::version_change_type
END;
-- Get user from session or auth context
BEGIN
v_user_id := current_setting('app.current_user_id', true)::UUID;
EXCEPTION WHEN OTHERS THEN
v_user_id := auth.uid();
END;
-- Convert NEW record to JSONB
v_version_data := to_jsonb(NEW);
-- Create version (only if we have a user context)
IF v_user_id IS NOT NULL THEN
PERFORM public.create_entity_version(
v_entity_type,
NEW.id,
v_version_data,
v_user_id,
CASE TG_OP
WHEN 'INSERT' THEN 'Entity created'
WHEN 'UPDATE' THEN 'Entity updated'
ELSE 'Entity modified'
END,
NULL,
v_change_type
);
ELSE
-- Log suspicious version without user (audit trail)
INSERT INTO public.admin_audit_log (
action,
details,
created_at
) VALUES (
'version_without_user',
jsonb_build_object(
'entity_type', v_entity_type,
'entity_id', NEW.id,
'table', TG_TABLE_NAME,
'operation', TG_OP,
'timestamp', NOW()
),
NOW()
);
END IF;
RETURN NEW;
END;
$$;