#!/usr/bin/env tsx /** * Check database permissions and provide helpful error messages * This script checks if the read-only user has SELECT permissions on required tables */ import { PrismaClient } from '@prisma/client'; import { prisma } from '../lib/db'; const REQUIRED_TABLES = [ 'photos', 'people', 'faces', 'person_encodings', 'tags', 'phototaglinkage', 'photo_favorites', ]; async function checkPermissions() { console.log('šŸ” Checking database permissions...\n'); // Extract username from DATABASE_URL const dbUrl = process.env.DATABASE_URL; if (!dbUrl) { console.error('āŒ DATABASE_URL not found in environment variables'); console.log('\nPlease add DATABASE_URL to your .env file:'); console.log('DATABASE_URL="postgresql://username:password@localhost:5432/punimtag"'); process.exit(1); } const match = dbUrl.match(/postgresql:\/\/([^:]+):/); if (!match) { console.error('āŒ Could not parse username from DATABASE_URL'); process.exit(1); } const username = match[1]; console.log(`šŸ“‹ Checking permissions for user: ${username}\n`); const errors: string[] = []; const successes: string[] = []; // Test each required table for (const table of REQUIRED_TABLES) { try { // Try to query the table let query: any; switch (table) { case 'photos': query = prisma.photo.findFirst(); break; case 'people': query = prisma.person.findFirst(); break; case 'faces': query = prisma.face.findFirst(); break; case 'person_encodings': // Skip person_encodings if not in schema try { query = (prisma as any).personEncoding?.findFirst(); if (!query) continue; } catch { continue; } break; case 'tags': query = prisma.tag.findFirst(); break; case 'phototaglinkage': query = prisma.photoTagLinkage.findFirst(); break; case 'photo_favorites': query = prisma.photoFavorite.findFirst(); break; default: continue; } if (query) { await query; successes.push(`āœ… ${table} - SELECT permission OK`); } } catch (error: any) { if (error.message?.includes('permission denied')) { errors.push(`āŒ ${table} - Permission denied`); } else if (error.message?.includes('does not exist')) { errors.push(`āš ļø ${table} - Table does not exist (may be OK if not used)`); } else { errors.push(`āŒ ${table} - ${error.message}`); } } } console.log('\nšŸ“Š Permission Check Results:\n'); successes.forEach((msg) => console.log(msg)); if (errors.length > 0) { console.log(''); errors.forEach((msg) => console.log(msg)); } if (errors.some((e) => e.includes('Permission denied'))) { console.log('\nāŒ Permission errors detected!\n'); console.log('To fix this, run the following SQL as a PostgreSQL superuser:\n'); console.log('```bash'); console.log(`psql -U postgres -d punimtag -f grant_readonly_permissions.sql`); console.log('```\n'); console.log('Or manually run:\n'); console.log('```sql'); console.log(`-- Connect to database`); console.log(`\\c punimtag`); console.log(''); console.log(`-- Grant permissions`); REQUIRED_TABLES.forEach((table) => { console.log(`GRANT SELECT ON TABLE ${table} TO ${username};`); }); console.log(`GRANT USAGE ON SCHEMA public TO ${username};`); console.log(`GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO ${username};`); console.log('```\n'); process.exit(1); } console.log('\nāœ… All required permissions are granted!'); process.exit(0); } checkPermissions().catch((error) => { console.error('Error checking permissions:', error); process.exit(1); });