mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-28 08:46:57 -05:00
Compare commits
4 Commits
71b174fe16
...
1180ae2b3b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1180ae2b3b | ||
|
|
949b502ec0 | ||
|
|
26e5ca6dbe | ||
|
|
dbe5ec2a07 |
@@ -162,7 +162,7 @@ export const useModerationQueue = (config?: UseModerationQueueConfig) => {
|
||||
const { data, error } = await supabase.rpc('extend_submission_lock', {
|
||||
submission_id: submissionId,
|
||||
moderator_id: user.id,
|
||||
extension_duration: '15 minutes',
|
||||
extension_duration: 'PT15M', // ISO 8601 format: 15 minutes
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
@@ -325,13 +325,34 @@ export const useModerationQueue = (config?: UseModerationQueueConfig) => {
|
||||
|
||||
const expiresAt = new Date(Date.now() + 15 * 60 * 1000);
|
||||
|
||||
const { data, error } = await supabase.rpc('claim_specific_submission', {
|
||||
p_submission_id: submissionId,
|
||||
p_moderator_id: user.id,
|
||||
p_lock_duration: '15 minutes',
|
||||
// Use direct fetch to force read-write transaction
|
||||
const supabaseUrl = 'https://api.thrillwiki.com';
|
||||
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkdnRtbnJzenlicW5iY3FiZGN5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTgzMjYzNTYsImV4cCI6MjA3MzkwMjM1Nn0.DM3oyapd_omP5ZzIlrT0H9qBsiQBxBRgw2tYuqgXKX4';
|
||||
|
||||
const { data: sessionData } = await supabase.auth.getSession();
|
||||
const token = sessionData.session?.access_token;
|
||||
|
||||
const response = await fetch(`${supabaseUrl}/rest/v1/rpc/claim_specific_submission`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'apikey': supabaseKey,
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Prefer': 'tx=commit', // Force read-write transaction
|
||||
},
|
||||
body: JSON.stringify({
|
||||
p_submission_id: submissionId,
|
||||
p_moderator_id: user.id,
|
||||
p_lock_duration: 'PT15M', // ISO 8601 format: 15 minutes
|
||||
}),
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({ message: 'Failed to claim submission' }));
|
||||
throw new Error(errorData.message || 'Failed to claim submission');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data) {
|
||||
throw new Error('Submission is already claimed or no longer available');
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
-- Update claim_specific_submission function to bypass RLS
|
||||
CREATE OR REPLACE FUNCTION public.claim_specific_submission(
|
||||
p_submission_id UUID,
|
||||
p_moderator_id UUID,
|
||||
p_lock_duration INTERVAL DEFAULT '15 minutes'
|
||||
) RETURNS BOOLEAN
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
rows_updated INTEGER;
|
||||
BEGIN
|
||||
-- Temporarily disable RLS for this transaction
|
||||
SET LOCAL row_security = off;
|
||||
|
||||
-- Atomically update the submission if it's unclaimed or lock expired
|
||||
UPDATE content_submissions
|
||||
SET
|
||||
assigned_to = p_moderator_id,
|
||||
assigned_at = NOW(),
|
||||
locked_until = NOW() + p_lock_duration,
|
||||
first_reviewed_at = COALESCE(first_reviewed_at, NOW())
|
||||
WHERE id = p_submission_id
|
||||
AND (
|
||||
assigned_to IS NULL
|
||||
OR locked_until < NOW()
|
||||
)
|
||||
AND status = 'pending';
|
||||
|
||||
GET DIAGNOSTICS rows_updated = ROW_COUNT;
|
||||
|
||||
-- Re-enable RLS (will auto-reset at end of transaction anyway)
|
||||
SET LOCAL row_security = on;
|
||||
|
||||
-- Log the action if successful
|
||||
IF rows_updated > 0 THEN
|
||||
BEGIN
|
||||
PERFORM log_admin_action(
|
||||
p_moderator_id,
|
||||
(SELECT user_id FROM content_submissions WHERE id = p_submission_id),
|
||||
'submission_claimed',
|
||||
jsonb_build_object(
|
||||
'submission_id', p_submission_id,
|
||||
'claim_type', 'specific'
|
||||
)
|
||||
);
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
RAISE WARNING 'Failed to log submission claim audit: %', SQLERRM;
|
||||
END;
|
||||
|
||||
RETURN TRUE;
|
||||
END IF;
|
||||
|
||||
RETURN FALSE;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,81 @@
|
||||
-- Fix notify_moderators_on_new_submission trigger to work with relational data model
|
||||
-- Removes references to non-existent NEW.content column
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.notify_moderators_on_new_submission()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $function$
|
||||
DECLARE
|
||||
submitter_profile record;
|
||||
base_url text;
|
||||
edge_function_url text;
|
||||
content_preview text;
|
||||
has_photos boolean := false;
|
||||
item_count integer := 0;
|
||||
BEGIN
|
||||
-- Get submitter's username or display name
|
||||
SELECT username, display_name INTO submitter_profile
|
||||
FROM public.profiles
|
||||
WHERE user_id = NEW.user_id;
|
||||
|
||||
-- Build simple content preview based on submission type
|
||||
content_preview := CASE NEW.submission_type
|
||||
WHEN 'park' THEN 'New park submission'
|
||||
WHEN 'ride' THEN 'New ride submission'
|
||||
WHEN 'company' THEN 'New company submission'
|
||||
WHEN 'ride_model' THEN 'New ride model submission'
|
||||
WHEN 'photo' THEN 'New photo submission'
|
||||
ELSE 'New submission'
|
||||
END;
|
||||
|
||||
-- Check if this submission has photos
|
||||
has_photos := EXISTS (
|
||||
SELECT 1 FROM photo_submissions ps
|
||||
WHERE ps.submission_id = NEW.id
|
||||
);
|
||||
|
||||
-- Count submission items
|
||||
SELECT COALESCE(COUNT(*), 0)::integer INTO item_count
|
||||
FROM submission_items
|
||||
WHERE submission_id = NEW.id;
|
||||
|
||||
-- Get base URL from settings
|
||||
SELECT setting_value::text INTO base_url
|
||||
FROM admin_settings
|
||||
WHERE setting_key = 'supabase_api_url';
|
||||
|
||||
base_url := trim(both '"' from base_url);
|
||||
|
||||
IF base_url IS NULL THEN
|
||||
base_url := 'https://api.thrillwiki.com';
|
||||
END IF;
|
||||
|
||||
edge_function_url := base_url || '/functions/v1/notify-moderators-submission';
|
||||
|
||||
-- Call the edge function via pg_net
|
||||
PERFORM public.pg_net.http_post(
|
||||
edge_function_url,
|
||||
jsonb_build_object(
|
||||
'submission_id', NEW.id,
|
||||
'submission_type', NEW.submission_type,
|
||||
'submitter_name', COALESCE(submitter_profile.display_name, submitter_profile.username, 'Unknown'),
|
||||
'action', 'create',
|
||||
'content_preview', content_preview,
|
||||
'submitted_at', NEW.created_at,
|
||||
'has_photos', has_photos,
|
||||
'item_count', item_count,
|
||||
'is_escalated', false
|
||||
)::text,
|
||||
'application/json'
|
||||
);
|
||||
|
||||
RETURN NEW;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
-- Log error but don't fail the submission
|
||||
RAISE NOTICE 'Failed to notify moderators: %', SQLERRM;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$;
|
||||
Reference in New Issue
Block a user