Skip to content
This repository was archived by the owner on Dec 14, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
540 changes: 540 additions & 0 deletions docs/supabase/ARCHITECTURE.md

Large diffs are not rendered by default.

822 changes: 822 additions & 0 deletions docs/supabase/RLS_POLICIES.md

Large diffs are not rendered by default.

778 changes: 778 additions & 0 deletions docs/supabase/TRIGGERS.md

Large diffs are not rendered by default.

831 changes: 831 additions & 0 deletions docs/supabase/TROUBLESHOOTING.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env dart

import 'dart:io';
import 'refactored_policy_generator.dart';
import 'refactored_trigger_generator.dart';
import 'policy_generator.dart';
import 'trigger_generator.dart';

/// 🗄️ Refactored Database Generator - Template-Based Approach
///
Expand All @@ -26,16 +26,16 @@ void main() async {
try {
// Generate RLS policies using templates
print('📋 Step 1: Generating RLS policies...');
final policyGenerator = RefactoredPolicyGenerator(
final policyGenerator = PolicyGenerator(
schemaPath: 'prisma/schema.prisma',
outputPath: 'scripts/supabase/generated/template_based_policies.sql',
);
await policyGenerator.generate();
print('✅ Policies generated successfully\n');

// Generate database triggers using templates
print('🔧 Step 2: Generating database triggers...');
final triggerGenerator = RefactoredTriggerGenerator(
final triggerGenerator = TriggerGenerator(
schemaPath: 'prisma/schema.prisma',
outputPath: 'scripts/supabase/generated/template_based_triggers.sql',
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ enum PolicyType {
consultantData, // Special handling for consultant-related data
}

class RefactoredPolicyGenerator {
class PolicyGenerator {
final String schemaPath;
final String outputPath;
final String templatesPath;
final List<PrismaModel> models = [];
RefactoredPolicyGenerator({

PolicyGenerator({
required this.schemaPath,
required this.outputPath,
this.templatesPath = 'scripts/supabase/templates',
Expand All @@ -63,14 +63,15 @@ class RefactoredPolicyGenerator {
await _parseSchema();

final buffer = StringBuffer();
buffer.writeln('-- 🛡️ Enhanced RLS Policies for Familiarise App');
buffer.writeln('-- 🛡️ Enhanced RLS Policies for Elluminar App');
buffer.writeln('-- Auto-generated using template-based approach');
buffer.writeln('-- Generated at: ${DateTime.now().toIso8601String()}');
buffer.writeln('-- Models processed: ${models.length}');
buffer.writeln();

await _generateHeader(buffer);
await _generateDropPolicies(buffer);
await _generateSchemaGrants(buffer);
await _generateEnableRLS(buffer);
await _generatePolicySection(buffer);
await _generateFooter(buffer);
Expand Down Expand Up @@ -274,24 +275,63 @@ class RefactoredPolicyGenerator {
buffer.writeln('-- 🗑️ DROP ALL EXISTING POLICIES (RERUNNABLE)');
buffer.writeln('-- ========================================');
buffer.writeln();

for (final model in models) {
final tableLower = model.tableLower;
buffer.writeln('-- Drop policies for ${model.name}');

// Drop old policy naming patterns (legacy)
buffer.writeln('DROP POLICY IF EXISTS "admin_access_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "user_access_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "public_read_$tableLower" ON "${model.tableName}";');


// Drop current policy naming patterns
// For public read tables with user ownership
buffer.writeln('DROP POLICY IF EXISTS "owner_insert_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "owner_update_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "owner_delete_$tableLower" ON "${model.tableName}";');

// For public read tables without user field
buffer.writeln('DROP POLICY IF EXISTS "authenticated_insert_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "authenticated_update_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "authenticated_delete_$tableLower" ON "${model.tableName}";');

// For user-specific tables
buffer.writeln('DROP POLICY IF EXISTS "user_select_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "user_insert_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "user_update_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "user_delete_$tableLower" ON "${model.tableName}";');

// For admin-only tables
buffer.writeln('DROP POLICY IF EXISTS "admin_select_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "admin_insert_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "admin_update_$tableLower" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "admin_delete_$tableLower" ON "${model.tableName}";');

// For authenticated user tables (like users table)
if (model.name == 'User') {
buffer.writeln('DROP POLICY IF EXISTS "authenticated_users_insert" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "authenticated_users_select" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "authenticated_users_update" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "authenticated_users_delete" ON "${model.tableName}";');
buffer.writeln('DROP POLICY IF EXISTS "public_profile_read_${model.tableName}" ON "${model.tableName}";');
}
}
buffer.writeln();
}


/// Generate schema grants using template
Future<void> _generateSchemaGrants(StringBuffer buffer) async {
buffer.writeln('-- ========================================');
buffer.writeln('-- 🔑 SCHEMA & TABLE PERMISSIONS');
buffer.writeln('-- ========================================');
buffer.writeln();

final schemaGrantsSQL = await _loadTemplate('schema_grants', {});
buffer.writeln(schemaGrantsSQL);
buffer.writeln();
}

/// Generate enable RLS section
Future<void> _generateEnableRLS(StringBuffer buffer) async {
buffer.writeln('-- ========================================');
Expand Down Expand Up @@ -341,6 +381,14 @@ class RefactoredPolicyGenerator {
final policySQL = await _loadTemplate(templateName, variables);
buffer.writeln(policySQL);
buffer.writeln();

// Special case: Add public profile read policy for User model
// This allows anonymous users to see consultant names/images for discovery
if (model.name == 'User') {
final publicProfileSQL = await _loadTemplate('public_profile_policy', variables);
buffer.writeln(publicProfileSQL);
buffer.writeln();
}
} catch (e) {
print('⚠️ Warning: Could not load template for ${model.name}: $e');
// Fallback to inline generation if template fails
Expand Down Expand Up @@ -418,11 +466,11 @@ class RefactoredPolicyGenerator {
}

void main() async {
final generator = RefactoredPolicyGenerator(
final generator = PolicyGenerator(
schemaPath: 'prisma/schema.prisma',
outputPath: 'scripts/supabase/generated/template_based_policies.sql',
);

try {
await generator.generate();
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import 'dart:io';

/// 🔧 Refactored Database Trigger Generator for Supabase
/// 🔧 Database Trigger Generator for Supabase
///
/// This generator focuses only on logic and uses SQL templates for content.
/// Templates are stored in scripts/supabase/templates/triggers/
Expand Down Expand Up @@ -42,13 +42,13 @@ enum TriggerType {
timestamp, // Timestamp update triggers
}

class RefactoredTriggerGenerator {
class TriggerGenerator {
final String schemaPath;
final String outputPath;
final String templatesPath;
final List<PrismaModel> models = [];
RefactoredTriggerGenerator({

TriggerGenerator({
required this.schemaPath,
required this.outputPath,
this.templatesPath = 'scripts/supabase/templates',
Expand All @@ -61,7 +61,7 @@ class RefactoredTriggerGenerator {
await _parseSchema();

final buffer = StringBuffer();
buffer.writeln('-- 🔧 Database Triggers for Familiarise App');
buffer.writeln('-- 🔧 Database Triggers for Elluminar App');
buffer.writeln('-- Auto-generated using template-based approach');
buffer.writeln('-- Generated at: ${DateTime.now().toIso8601String()}');
buffer.writeln('-- Models processed: ${models.length}');
Expand Down Expand Up @@ -146,11 +146,9 @@ class RefactoredTriggerGenerator {

/// Determine if model needs audit trigger
bool _needsAuditTrigger(String name) {
final auditModels = {
'User', 'ConsultantProfile', 'ConsulteeProfile', 'Payment',
'Appointment', 'Consultation', 'ConsultantReview'
};
return auditModels.contains(name);
// Audit logging disabled - no audit_logs table exists
// Re-enable when audit_logs table is created in Prisma schema
return false;
}

/// Load and process SQL template with conditional logic support
Expand Down Expand Up @@ -415,11 +413,11 @@ class RefactoredTriggerGenerator {
}

void main() async {
final generator = RefactoredTriggerGenerator(
final generator = TriggerGenerator(
schemaPath: 'prisma/schema.prisma',
outputPath: 'scripts/supabase/generated/template_based_triggers.sql',
);

try {
await generator.generate();
} catch (e) {
Expand Down
60 changes: 0 additions & 60 deletions scripts/supabase/legacy/README.md

This file was deleted.

Loading
Loading