Tanya de2144be2a feat: Add new scripts and update project structure for database management and user authentication
This commit introduces several new scripts for managing database operations, including user creation, permission grants, and data migrations. It also adds new documentation files to guide users through the setup and configuration processes. Additionally, the project structure is updated to enhance organization and maintainability, ensuring a smoother development experience for contributors. These changes support the ongoing transition to a web-based architecture and improve overall project functionality.
2026-01-06 13:53:24 -05:00

133 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient as PrismaClientAuth } from '../node_modules/.prisma/client-auth';
import bcrypt from 'bcryptjs';
import * as dotenv from 'dotenv';
// Load environment variables
dotenv.config({ path: '.env' });
const prisma = new PrismaClientAuth({
datasourceUrl: process.env.DATABASE_URL_AUTH,
});
async function setupAuth() {
try {
console.log('Setting up authentication tables and admin user...\n');
// Create tables using raw SQL (Prisma doesn't support CREATE TABLE in migrations easily)
console.log('Creating users table...');
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255),
password_hash VARCHAR(255) NOT NULL,
is_admin BOOLEAN DEFAULT FALSE,
is_active BOOLEAN DEFAULT TRUE,
has_write_access BOOLEAN DEFAULT FALSE,
email_verified BOOLEAN DEFAULT FALSE,
email_confirmation_token VARCHAR(255) UNIQUE,
email_confirmation_token_expiry TIMESTAMP,
password_reset_token VARCHAR(255) UNIQUE,
password_reset_token_expiry TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`);
// Add missing columns if table already exists with old schema
console.log('Adding missing columns if needed...');
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS is_admin BOOLEAN DEFAULT FALSE;`);
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS has_write_access BOOLEAN DEFAULT FALSE;`);
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified BOOLEAN DEFAULT FALSE;`);
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS email_confirmation_token VARCHAR(255);`);
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS email_confirmation_token_expiry TIMESTAMP;`);
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS password_reset_token VARCHAR(255);`);
await prisma.$executeRawUnsafe(`ALTER TABLE users ADD COLUMN IF NOT EXISTS password_reset_token_expiry TIMESTAMP;`);
// Add unique constraints if they don't exist
await prisma.$executeRawUnsafe(`CREATE UNIQUE INDEX IF NOT EXISTS users_email_confirmation_token_key ON users(email_confirmation_token) WHERE email_confirmation_token IS NOT NULL;`);
await prisma.$executeRawUnsafe(`CREATE UNIQUE INDEX IF NOT EXISTS users_password_reset_token_key ON users(password_reset_token) WHERE password_reset_token IS NOT NULL;`);
console.log('Creating pending_identifications table...');
await prisma.$executeRawUnsafe(`
CREATE TABLE IF NOT EXISTS pending_identifications (
id SERIAL PRIMARY KEY,
face_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
middle_name VARCHAR(255),
maiden_name VARCHAR(255),
date_of_birth DATE,
status VARCHAR(50) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
-- Note: face_id references faces in punimtag database, but we can't use foreign key across databases
);
`);
console.log('Creating indexes...');
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS idx_pending_identifications_face_id ON pending_identifications(face_id);`);
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS idx_pending_identifications_user_id ON pending_identifications(user_id);`);
await prisma.$executeRawUnsafe(`CREATE INDEX IF NOT EXISTS idx_pending_identifications_status ON pending_identifications(status);`);
// Check if admin user already exists
const existingAdmin = await prisma.user.findUnique({
where: { email: 'admin@admin.com' },
});
if (existingAdmin) {
console.log('Admin user already exists. Updating password...');
const passwordHash = await bcrypt.hash('admin', 10);
await prisma.user.update({
where: { email: 'admin@admin.com' },
data: {
passwordHash,
isAdmin: true,
hasWriteAccess: true,
emailVerified: true,
isActive: true,
},
});
console.log('✅ Admin user password updated (admin@admin.com / admin)');
} else {
console.log('Creating admin user...');
const passwordHash = await bcrypt.hash('admin', 10);
await prisma.user.create({
data: {
email: 'admin@admin.com',
name: 'Admin',
passwordHash,
isAdmin: true,
hasWriteAccess: true,
emailVerified: true,
isActive: true,
},
});
console.log('✅ Admin user created (admin@admin.com / admin)');
}
console.log('\n✅ Setup complete!');
console.log('\nAdmin credentials:');
console.log(' Email: admin@admin.com');
console.log(' Password: admin');
console.log('\nNote: Make sure to grant appropriate database permissions:');
console.log(' - Regular users: INSERT on pending_identifications');
console.log(' - Admin: UPDATE on pending_identifications (for approval)');
} catch (error: any) {
console.error('Error setting up authentication:', error);
if (error.message.includes('permission denied')) {
console.error('\n⚠ Permission denied. You may need to:');
console.error(' 1. Run this script with a user that has CREATE TABLE permissions');
console.error(' 2. Or manually create the tables using create_auth_tables.sql');
}
throw error;
} finally {
await prisma.$disconnect();
}
}
setupAuth();