diff --git a/api/auth/register.ts b/api/auth/register.ts index 1c2bc30..365896c 100644 --- a/api/auth/register.ts +++ b/api/auth/register.ts @@ -24,6 +24,21 @@ export default async function handler(req: NextRequest) { }); } + // Check environment variables + if (!process.env.DATABASE_URL) { + return new NextResponse(JSON.stringify({ + error: 'Database configuration missing', + details: 'DATABASE_URL environment variable not set' + }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + + if (!process.env.JWT_SECRET) { + console.warn('JWT_SECRET not set, using fallback'); + } + const client = await pool.connect(); try { @@ -62,6 +77,25 @@ export default async function handler(req: NextRequest) { }); } + // Check if users table exists + const tableCheck = await client.query(` + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'users' + ); + `); + + if (!tableCheck.rows[0].exists) { + return new NextResponse(JSON.stringify({ + error: 'Database not initialized', + details: 'Please run the setup-auth endpoint first' + }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + // Check if user already exists const existingUser = await client.query( 'SELECT id FROM users WHERE username = $1 OR email = $2', diff --git a/api/setup-auth.ts b/api/setup-auth.ts index 1e1e38b..019de07 100644 --- a/api/setup-auth.ts +++ b/api/setup-auth.ts @@ -19,9 +19,28 @@ export default async function handler(req: NextRequest) { try { console.log('Setting up user authentication schema...'); - // Execute the schema setup + // Check if users table already exists + const tableCheck = await client.query(` + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'users' + ); + `); + + if (tableCheck.rows[0].exists) { + return new NextResponse(JSON.stringify({ + success: true, + message: 'User authentication schema already exists', + already_setup: true + }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + } + + // Create tables one by one await client.query(` - -- Users table CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, @@ -36,16 +55,18 @@ export default async function handler(req: NextRequest) { updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, last_login TIMESTAMP WITH TIME ZONE ); + `); - -- Roles table + await client.query(` CREATE TABLE IF NOT EXISTS roles ( id SERIAL PRIMARY KEY, name VARCHAR(50) UNIQUE NOT NULL, description TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); + `); - -- User roles junction table (many-to-many) + await client.query(` CREATE TABLE IF NOT EXISTS user_roles ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, @@ -54,8 +75,9 @@ export default async function handler(req: NextRequest) { assigned_by INTEGER REFERENCES users(id), UNIQUE(user_id, role_id) ); + `); - -- Permissions table + await client.query(` CREATE TABLE IF NOT EXISTS permissions ( id SERIAL PRIMARY KEY, name VARCHAR(100) UNIQUE NOT NULL, @@ -64,16 +86,18 @@ export default async function handler(req: NextRequest) { action VARCHAR(50), created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); + `); - -- Role permissions junction table + await client.query(` CREATE TABLE IF NOT EXISTS role_permissions ( id SERIAL PRIMARY KEY, role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE, permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE, UNIQUE(role_id, permission_id) ); + `); - -- Sessions table for JWT token management + await client.query(` CREATE TABLE IF NOT EXISTS user_sessions ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, @@ -86,12 +110,6 @@ export default async function handler(req: NextRequest) { ); `); - // Update existing tables to include user ownership - await client.query(` - ALTER TABLE user_collections ADD COLUMN IF NOT EXISTS created_by INTEGER REFERENCES users(id); - ALTER TABLE user_decks ADD COLUMN IF NOT EXISTS created_by INTEGER REFERENCES users(id); - `); - // Insert default roles await client.query(` INSERT INTO roles (name, description) VALUES @@ -100,51 +118,32 @@ export default async function handler(req: NextRequest) { ON CONFLICT (name) DO NOTHING; `); - // Insert default permissions + // Insert basic permissions const permissions = [ - ['cards.read', 'View cards', 'cards', 'read'], - ['cards.create', 'Create new cards', 'cards', 'create'], - ['cards.update', 'Update existing cards', 'cards', 'update'], - ['cards.delete', 'Delete cards', 'cards', 'delete'], - ['collections.read', 'View collections', 'collections', 'read'], - ['collections.create', 'Create collections', 'collections', 'create'], - ['collections.update', 'Update collections', 'collections', 'update'], - ['collections.delete', 'Delete collections', 'collections', 'delete'], - ['decks.read', 'View decks', 'decks', 'read'], - ['decks.create', 'Create decks', 'decks', 'create'], - ['decks.update', 'Update decks', 'decks', 'update'], - ['decks.delete', 'Delete decks', 'decks', 'delete'], - ['users.read', 'View users', 'users', 'read'], - ['users.create', 'Create users', 'users', 'create'], - ['users.update', 'Update users', 'users', 'update'], - ['users.delete', 'Delete users', 'users', 'delete'], - ['admin.access', 'Access admin panel', 'admin', 'access'] + ['cards.read', 'View cards'], + ['collections.manage', 'Manage collections'], + ['decks.manage', 'Manage decks'], + ['admin.access', 'Access admin panel'] ]; - for (const [name, description, resource, action] of permissions) { + for (const [name, description] of permissions) { await client.query(` - INSERT INTO permissions (name, description, resource, action) - VALUES ($1, $2, $3, $4) + INSERT INTO permissions (name, description) + VALUES ($1, $2) ON CONFLICT (name) DO NOTHING; - `, [name, description, resource, action]); + `, [name, description]); } // Assign permissions to roles - // User role permissions await client.query(` INSERT INTO role_permissions (role_id, permission_id) SELECT r.id, p.id FROM roles r, permissions p WHERE r.name = 'user' - AND p.name IN ( - 'cards.read', - 'collections.read', 'collections.create', 'collections.update', 'collections.delete', - 'decks.read', 'decks.create', 'decks.update', 'decks.delete' - ) + AND p.name IN ('cards.read', 'collections.manage', 'decks.manage') ON CONFLICT (role_id, permission_id) DO NOTHING; `); - // Admin role permissions (all permissions) await client.query(` INSERT INTO role_permissions (role_id, permission_id) SELECT r.id, p.id @@ -153,32 +152,11 @@ export default async function handler(req: NextRequest) { ON CONFLICT (role_id, permission_id) DO NOTHING; `); - // Create indexes for performance + // Create indexes await client.query(` CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles(user_id); - CREATE INDEX IF NOT EXISTS idx_user_sessions_user_id ON user_sessions(user_id); - CREATE INDEX IF NOT EXISTS idx_user_sessions_expires_at ON user_sessions(expires_at); - `); - - // Create updated_at trigger function - await client.query(` - CREATE OR REPLACE FUNCTION update_updated_at_column() - RETURNS TRIGGER AS $$ - BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; - END; - $$ language 'plpgsql'; - `); - - // Add updated_at triggers - await client.query(` - DROP TRIGGER IF EXISTS update_users_updated_at ON users; - CREATE TRIGGER update_users_updated_at - BEFORE UPDATE ON users - FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); `); console.log('✅ User authentication schema setup completed!'); diff --git a/api/test-db.ts b/api/test-db.ts new file mode 100644 index 0000000..553737e --- /dev/null +++ b/api/test-db.ts @@ -0,0 +1,86 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { Pool } from 'pg'; + +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, +}); + +export default async function handler(req: NextRequest) { + if (req.method !== 'GET') { + return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), { + status: 405, + headers: { 'Content-Type': 'application/json' }, + }); + } + + const client = await pool.connect(); + + try { + console.log('Testing database connection...'); + + // Test basic connection + const result = await client.query('SELECT NOW() as current_time, version() as postgres_version'); + + // Check if users table exists + const tableCheck = await client.query(` + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'users' + ) as users_table_exists; + `); + + // Check if cards table exists + const cardsCheck = await client.query(` + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'cards' + ) as cards_table_exists; + `); + + // Get table counts + let userCount = 0; + let cardCount = 0; + + if (tableCheck.rows[0].users_table_exists) { + const userCountResult = await client.query('SELECT COUNT(*) as count FROM users'); + userCount = parseInt(userCountResult.rows[0].count); + } + + if (cardsCheck.rows[0].cards_table_exists) { + const cardCountResult = await client.query('SELECT COUNT(*) as count FROM cards'); + cardCount = parseInt(cardCountResult.rows[0].count); + } + + return new NextResponse(JSON.stringify({ + success: true, + message: 'Database connection successful', + database_info: { + current_time: result.rows[0].current_time, + postgres_version: result.rows[0].postgres_version, + users_table_exists: tableCheck.rows[0].users_table_exists, + cards_table_exists: cardsCheck.rows[0].cards_table_exists, + user_count: userCount, + card_count: cardCount + } + }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + + } catch (error) { + console.error('Database connection failed:', error); + return new NextResponse(JSON.stringify({ + success: false, + error: 'Database connection failed', + details: (error as Error).message + }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } finally { + client.release(); + } +} \ No newline at end of file