Fix RLS policy for JSONB migration

This commit is contained in:
gpt-engineer-app[bot]
2025-11-03 20:42:30 +00:00
parent 3d07198454
commit 223e743330
3 changed files with 1013 additions and 0 deletions

193
src/lib/auditHelpers.ts Normal file
View File

@@ -0,0 +1,193 @@
/**
* Helper functions for relational audit logging
* Replaces JSONB storage with proper relational tables
*/
import { supabase } from '@/integrations/supabase/client';
import { logger } from './logger';
/**
* Write admin audit details to relational table
* Replaces JSONB admin_audit_log.details column
*/
export async function writeAdminAuditDetails(
auditLogId: string,
details: Record<string, unknown>
): Promise<void> {
if (!details || Object.keys(details).length === 0) return;
const entries = Object.entries(details).map(([key, value]) => ({
audit_log_id: auditLogId,
detail_key: key,
detail_value: typeof value === 'object' ? JSON.stringify(value) : String(value),
}));
const { error } = await supabase
.from('admin_audit_details')
.insert(entries);
if (error) {
logger.error('Failed to write admin audit details', { error, auditLogId });
throw error;
}
}
/**
* Write moderation audit metadata to relational table
* Replaces JSONB moderation_audit_log.metadata column
*/
export async function writeModerationAuditMetadata(
auditLogId: string,
metadata: Record<string, unknown>
): Promise<void> {
if (!metadata || Object.keys(metadata).length === 0) return;
const entries = Object.entries(metadata).map(([key, value]) => ({
audit_log_id: auditLogId,
metadata_key: key,
metadata_value: typeof value === 'object' ? JSON.stringify(value) : String(value),
}));
const { error } = await supabase
.from('moderation_audit_metadata')
.insert(entries);
if (error) {
logger.error('Failed to write moderation audit metadata', { error, auditLogId });
throw error;
}
}
/**
* Write item change fields to relational table
* Replaces JSONB item_edit_history.changes column
*/
export async function writeItemChangeFields(
editHistoryId: string,
changes: Record<string, { old_value?: unknown; new_value?: unknown }>
): Promise<void> {
if (!changes || Object.keys(changes).length === 0) return;
const entries = Object.entries(changes).map(([fieldName, change]) => ({
edit_history_id: editHistoryId,
field_name: fieldName,
old_value: change.old_value !== undefined
? (typeof change.old_value === 'object' ? JSON.stringify(change.old_value) : String(change.old_value))
: null,
new_value: change.new_value !== undefined
? (typeof change.new_value === 'object' ? JSON.stringify(change.new_value) : String(change.new_value))
: null,
}));
const { error } = await supabase
.from('item_change_fields')
.insert(entries);
if (error) {
logger.error('Failed to write item change fields', { error, editHistoryId });
throw error;
}
}
/**
* Write request breadcrumbs to relational table
* Replaces JSONB request_metadata.breadcrumbs column
*/
export async function writeRequestBreadcrumbs(
requestId: string,
breadcrumbs: Array<{
timestamp: string;
category: string;
message: string;
level?: 'debug' | 'info' | 'warn' | 'error';
}>
): Promise<void> {
if (!breadcrumbs || breadcrumbs.length === 0) return;
const entries = breadcrumbs.map((breadcrumb, index) => ({
request_id: requestId,
timestamp: breadcrumb.timestamp,
category: breadcrumb.category,
message: breadcrumb.message,
level: breadcrumb.level || 'info',
sequence_order: index,
}));
const { error } = await supabase
.from('request_breadcrumbs')
.insert(entries);
if (error) {
logger.error('Failed to write request breadcrumbs', { error, requestId });
throw error;
}
}
/**
* Read admin audit details from relational table
*/
export async function readAdminAuditDetails(
auditLogId: string
): Promise<Record<string, string>> {
const { data, error } = await supabase
.from('admin_audit_details')
.select('detail_key, detail_value')
.eq('audit_log_id', auditLogId);
if (error) {
logger.error('Failed to read admin audit details', { error, auditLogId });
return {};
}
return data.reduce((acc, row) => {
acc[row.detail_key] = row.detail_value;
return acc;
}, {} as Record<string, string>);
}
/**
* Read moderation audit metadata from relational table
*/
export async function readModerationAuditMetadata(
auditLogId: string
): Promise<Record<string, string>> {
const { data, error } = await supabase
.from('moderation_audit_metadata')
.select('metadata_key, metadata_value')
.eq('audit_log_id', auditLogId);
if (error) {
logger.error('Failed to read moderation audit metadata', { error, auditLogId });
return {};
}
return data.reduce((acc, row) => {
acc[row.metadata_key] = row.metadata_value;
return acc;
}, {} as Record<string, string>);
}
/**
* Read item change fields from relational table
*/
export async function readItemChangeFields(
editHistoryId: string
): Promise<Record<string, { old_value: string | null; new_value: string | null }>> {
const { data, error } = await supabase
.from('item_change_fields')
.select('field_name, old_value, new_value')
.eq('edit_history_id', editHistoryId);
if (error) {
logger.error('Failed to read item change fields', { error, editHistoryId });
return {};
}
return data.reduce((acc, row) => {
acc[row.field_name] = {
old_value: row.old_value,
new_value: row.new_value,
};
return acc;
}, {} as Record<string, { old_value: string | null; new_value: string | null }>);
}