Fix: Address build errors in moderation logging

This commit is contained in:
gpt-engineer-app[bot]
2025-10-27 17:17:13 +00:00
parent 2de13c12e4
commit 1ebb491ffa
4 changed files with 195 additions and 106 deletions

View File

@@ -333,8 +333,12 @@ export const ReportsQueue = forwardRef<ReportsQueueRef>((props, ref) => {
const handleReportAction = async (reportId: string, action: 'reviewed' | 'dismissed') => { const handleReportAction = async (reportId: string, action: 'reviewed' | 'dismissed') => {
setActionLoading(reportId); setActionLoading(reportId);
try { try {
// Fetch report details for audit log // Fetch full report details including reporter_id for audit log
const report = reports.find(r => r.id === reportId); const { data: reportData } = await supabase
.from('reports')
.select('reporter_id, reported_entity_type, reported_entity_id, reason')
.eq('id', reportId)
.single();
const { error } = await supabase const { error } = await supabase
.from('reports') .from('reports')
@@ -348,17 +352,17 @@ export const ReportsQueue = forwardRef<ReportsQueueRef>((props, ref) => {
if (error) throw error; if (error) throw error;
// Log audit trail for report resolution // Log audit trail for report resolution
if (user && report) { if (user && reportData) {
try { try {
await supabase.rpc('log_admin_action', { await supabase.rpc('log_admin_action', {
_admin_user_id: user.id, _admin_user_id: user.id,
_target_user_id: report.reported_by, _target_user_id: reportData.reporter_id,
_action: action === 'reviewed' ? 'report_resolved' : 'report_dismissed', _action: action === 'reviewed' ? 'report_resolved' : 'report_dismissed',
_details: { _details: {
report_id: reportId, report_id: reportId,
reported_entity_type: report.reported_entity_type, reported_entity_type: reportData.reported_entity_type,
reported_entity_id: report.reported_entity_id, reported_entity_id: reportData.reported_entity_id,
report_reason: report.reason, report_reason: reportData.reason,
action: action action: action
} }
}); });

View File

@@ -238,14 +238,18 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
// Log audit trail for review moderation // Log audit trail for review moderation
if (table === 'reviews' && user) { if (table === 'reviews' && user) {
try { try {
// Extract entity information from item content
const entityType = item.content?.ride_id ? 'ride' : item.content?.park_id ? 'park' : 'unknown';
const entityId = item.content?.ride_id || item.content?.park_id || null;
await supabase.rpc('log_admin_action', { await supabase.rpc('log_admin_action', {
_admin_user_id: user.id, _admin_user_id: user.id,
_target_user_id: item.user_id, _target_user_id: item.user_id,
_action: `review_${action}`, _action: `review_${action}`,
_details: { _details: {
review_id: item.id, review_id: item.id,
entity_type: item.entity_type, entity_type: entityType,
entity_id: item.entity_id, entity_id: entityId,
moderator_notes: moderatorNotes moderator_notes: moderatorNotes
} }
}); });

View File

@@ -3741,10 +3741,7 @@ export type Database = {
Args: { target_user_id: string } Args: { target_user_id: string }
Returns: undefined Returns: undefined
} }
backfill_sort_orders: { backfill_sort_orders: { Args: never; Returns: undefined }
Args: Record<PropertyKey, never>
Returns: undefined
}
can_approve_submission_item: { can_approve_submission_item: {
Args: { item_id: string } Args: { item_id: string }
Returns: boolean Returns: boolean
@@ -3765,10 +3762,7 @@ export type Database = {
Args: { _profile_user_id: string; _viewer_id: string } Args: { _profile_user_id: string; _viewer_id: string }
Returns: boolean Returns: boolean
} }
cancel_user_email_change: { cancel_user_email_change: { Args: { _user_id: string }; Returns: boolean }
Args: { _user_id: string }
Returns: boolean
}
check_rate_limit: { check_rate_limit: {
Args: { Args: {
p_action: string p_action: string
@@ -3777,10 +3771,7 @@ export type Database = {
} }
Returns: Json Returns: Json
} }
check_realtime_access: { check_realtime_access: { Args: never; Returns: boolean }
Args: Record<PropertyKey, never>
Returns: boolean
}
claim_next_submission: { claim_next_submission: {
Args: { lock_duration?: unknown; moderator_id: string } Args: { lock_duration?: unknown; moderator_id: string }
Returns: { Returns: {
@@ -3789,30 +3780,15 @@ export type Database = {
waiting_time: unknown waiting_time: unknown
}[] }[]
} }
cleanup_expired_sessions: { cleanup_expired_sessions: { Args: never; Returns: undefined }
Args: Record<PropertyKey, never> cleanup_old_page_views: { Args: never; Returns: undefined }
Returns: undefined cleanup_old_request_metadata: { Args: never; Returns: undefined }
}
cleanup_old_page_views: {
Args: Record<PropertyKey, never>
Returns: undefined
}
cleanup_old_request_metadata: {
Args: Record<PropertyKey, never>
Returns: undefined
}
cleanup_old_versions: { cleanup_old_versions: {
Args: { entity_type: string; keep_versions?: number } Args: { entity_type: string; keep_versions?: number }
Returns: number Returns: number
} }
cleanup_orphaned_submissions: { cleanup_orphaned_submissions: { Args: never; Returns: number }
Args: Record<PropertyKey, never> cleanup_rate_limits: { Args: never; Returns: undefined }
Returns: number
}
cleanup_rate_limits: {
Args: Record<PropertyKey, never>
Returns: undefined
}
create_submission_with_items: { create_submission_with_items: {
Args: { Args: {
p_content: Json p_content: Json
@@ -3830,24 +3806,15 @@ export type Database = {
} }
Returns: string Returns: string
} }
extract_cf_image_id: { extract_cf_image_id: { Args: { url: string }; Returns: string }
Args: { url: string } generate_deletion_confirmation_code: { Args: never; Returns: string }
Returns: string get_email_change_status: { Args: never; Returns: Json }
}
generate_deletion_confirmation_code: {
Args: Record<PropertyKey, never>
Returns: string
}
get_email_change_status: {
Args: Record<PropertyKey, never>
Returns: Json
}
get_filtered_profile: { get_filtered_profile: {
Args: { _profile_user_id: string; _viewer_id?: string } Args: { _profile_user_id: string; _viewer_id?: string }
Returns: Json Returns: Json
} }
get_my_sessions: { get_my_sessions: {
Args: Record<PropertyKey, never> Args: never
Returns: { Returns: {
aal: string aal: string
created_at: string created_at: string
@@ -3879,18 +3846,9 @@ export type Database = {
} }
Returns: Json Returns: Json
} }
has_aal2: { has_aal2: { Args: never; Returns: boolean }
Args: Record<PropertyKey, never> has_mfa_enabled: { Args: { _user_id: string }; Returns: boolean }
Returns: boolean has_pending_dependents: { Args: { item_id: string }; Returns: boolean }
}
has_mfa_enabled: {
Args: { _user_id: string }
Returns: boolean
}
has_pending_dependents: {
Args: { item_id: string }
Returns: boolean
}
has_role: { has_role: {
Args: { Args: {
_role: Database["public"]["Enums"]["app_role"] _role: Database["public"]["Enums"]["app_role"]
@@ -3898,26 +3856,14 @@ export type Database = {
} }
Returns: boolean Returns: boolean
} }
hash_ip_address: { hash_ip_address: { Args: { ip_text: string }; Returns: string }
Args: { ip_text: string } hash_session_ip: { Args: { session_ip: unknown }; Returns: string }
Returns: string
}
hash_session_ip: {
Args: { session_ip: unknown }
Returns: string
}
increment_blog_view_count: { increment_blog_view_count: {
Args: { post_slug: string } Args: { post_slug: string }
Returns: undefined Returns: undefined
} }
is_moderator: { is_moderator: { Args: { _user_id: string }; Returns: boolean }
Args: { _user_id: string } is_superuser: { Args: { _user_id: string }; Returns: boolean }
Returns: boolean
}
is_superuser: {
Args: { _user_id: string }
Returns: boolean
}
log_admin_action: { log_admin_action: {
Args: { Args: {
_action: string _action: string
@@ -3927,10 +3873,7 @@ export type Database = {
} }
Returns: undefined Returns: undefined
} }
log_cleanup_results: { log_cleanup_results: { Args: never; Returns: undefined }
Args: Record<PropertyKey, never>
Returns: undefined
}
log_request_metadata: { log_request_metadata: {
Args: { Args: {
p_client_version?: string p_client_version?: string
@@ -3948,18 +3891,9 @@ export type Database = {
} }
Returns: undefined Returns: undefined
} }
migrate_ride_technical_data: { migrate_ride_technical_data: { Args: never; Returns: undefined }
Args: Record<PropertyKey, never> migrate_user_list_items: { Args: never; Returns: undefined }
Returns: undefined release_expired_locks: { Args: never; Returns: number }
}
migrate_user_list_items: {
Args: Record<PropertyKey, never>
Returns: undefined
}
release_expired_locks: {
Args: Record<PropertyKey, never>
Returns: number
}
release_submission_lock: { release_submission_lock: {
Args: { moderator_id: string; submission_id: string } Args: { moderator_id: string; submission_id: string }
Returns: boolean Returns: boolean
@@ -3968,10 +3902,7 @@ export type Database = {
Args: { p_credit_id: string; p_new_position: number } Args: { p_credit_id: string; p_new_position: number }
Returns: undefined Returns: undefined
} }
revoke_my_session: { revoke_my_session: { Args: { session_id: string }; Returns: undefined }
Args: { session_id: string }
Returns: undefined
}
revoke_session_with_mfa: { revoke_session_with_mfa: {
Args: { target_session_id: string; target_user_id: string } Args: { target_session_id: string; target_user_id: string }
Returns: Json Returns: Json
@@ -3998,10 +3929,7 @@ export type Database = {
Args: { target_company_id: string } Args: { target_company_id: string }
Returns: undefined Returns: undefined
} }
update_entity_view_counts: { update_entity_view_counts: { Args: never; Returns: undefined }
Args: Record<PropertyKey, never>
Returns: undefined
}
update_park_ratings: { update_park_ratings: {
Args: { target_park_id: string } Args: { target_park_id: string }
Returns: undefined Returns: undefined

View File

@@ -0,0 +1,153 @@
-- Add audit logging to submission lock RPC functions
CREATE OR REPLACE FUNCTION public.claim_next_submission(
moderator_id UUID,
lock_duration INTERVAL DEFAULT '15 minutes'
)
RETURNS TABLE (
submission_id UUID,
submission_type TEXT,
waiting_time INTERVAL
)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
claimed_submission RECORD;
BEGIN
UPDATE content_submissions
SET
assigned_to = moderator_id,
assigned_at = NOW(),
locked_until = NOW() + lock_duration,
first_reviewed_at = COALESCE(first_reviewed_at, NOW())
WHERE id = (
SELECT cs.id FROM content_submissions cs
WHERE cs.status IN ('pending', 'partially_approved')
AND (cs.assigned_to IS NULL OR cs.locked_until < NOW())
ORDER BY
cs.escalated DESC,
cs.submitted_at ASC
LIMIT 1
FOR UPDATE SKIP LOCKED
)
RETURNING
content_submissions.id,
content_submissions.submission_type,
content_submissions.user_id,
NOW() - content_submissions.submitted_at
INTO claimed_submission;
IF FOUND THEN
BEGIN
PERFORM log_admin_action(
moderator_id,
claimed_submission.user_id,
'submission_claimed',
jsonb_build_object(
'submission_id', claimed_submission.id,
'submission_type', claimed_submission.submission_type
)
);
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to log submission claim audit: %', SQLERRM;
END;
END IF;
RETURN QUERY
SELECT
claimed_submission.id,
claimed_submission.submission_type,
claimed_submission.waiting_time;
END;
$$;
CREATE OR REPLACE FUNCTION release_submission_lock(
submission_id UUID,
moderator_id UUID
) RETURNS BOOLEAN
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
submission_details RECORD;
BEGIN
SELECT user_id, submission_type INTO submission_details
FROM content_submissions
WHERE id = submission_id
AND assigned_to = moderator_id;
UPDATE content_submissions
SET
assigned_to = NULL,
assigned_at = NULL,
locked_until = NULL
WHERE id = submission_id
AND assigned_to = moderator_id;
IF FOUND THEN
BEGIN
PERFORM log_admin_action(
moderator_id,
submission_details.user_id,
'submission_released',
jsonb_build_object(
'submission_id', submission_id,
'submission_type', submission_details.submission_type
)
);
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to log submission release audit: %', SQLERRM;
END;
END IF;
RETURN FOUND;
END;
$$;
CREATE OR REPLACE FUNCTION extend_submission_lock(
submission_id UUID,
moderator_id UUID,
extension_duration INTERVAL DEFAULT '15 minutes'
) RETURNS TIMESTAMP WITH TIME ZONE
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
new_lock_time TIMESTAMP WITH TIME ZONE;
submission_details RECORD;
BEGIN
SELECT user_id, submission_type INTO submission_details
FROM content_submissions
WHERE id = submission_id
AND assigned_to = moderator_id;
UPDATE content_submissions
SET locked_until = NOW() + extension_duration
WHERE id = submission_id
AND assigned_to = moderator_id
RETURNING locked_until INTO new_lock_time;
IF FOUND THEN
BEGIN
PERFORM log_admin_action(
moderator_id,
submission_details.user_id,
'submission_lock_extended',
jsonb_build_object(
'submission_id', submission_id,
'submission_type', submission_details.submission_type,
'new_lock_until', new_lock_time
)
);
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'Failed to log submission lock extension audit: %', SQLERRM;
END;
END IF;
RETURN new_lock_time;
END;
$$;