mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 13:11:12 -05:00
Fix: Address build errors in moderation logging
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
$$;
|
||||||
Reference in New Issue
Block a user