From 299fb1916ba2cc4986981d5c777ae526f2135b9e Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Tue, 22 Jul 2025 06:55:09 -0500 Subject: [PATCH] Remove conflicting TypeScript API files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🔧 Build Fix: - Remove .ts versions of API files that conflict with .js versions - Keep only working JavaScript API endpoints: - api/auth/login.js - api/auth/register.js - api/setup-auth.js - api/simple.js - api/debug.js This resolves the Vercel build error about conflicting file paths. --- api/auth/login.ts | 163 ---------------------------------- api/auth/register.ts | 205 ------------------------------------------- api/health.ts | 17 ---- api/hello.ts | 20 ----- api/setup-auth.ts | 185 -------------------------------------- api/simple.ts | 6 -- api/test-db.ts | 111 ----------------------- 7 files changed, 707 deletions(-) delete mode 100644 api/auth/login.ts delete mode 100644 api/auth/register.ts delete mode 100644 api/health.ts delete mode 100644 api/hello.ts delete mode 100644 api/setup-auth.ts delete mode 100644 api/simple.ts delete mode 100644 api/test-db.ts diff --git a/api/auth/login.ts b/api/auth/login.ts deleted file mode 100644 index b97d823..0000000 --- a/api/auth/login.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { Pool } from 'pg'; -import bcrypt from 'bcryptjs'; -import jwt from 'jsonwebtoken'; - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, -}); - -interface LoginRequest { - username: string; - password: string; -} - -export default async function handler(req: NextRequest) { - if (req.method !== 'POST') { - return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), { - status: 405, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const client = await pool.connect(); - - try { - // Parse request body - let body: LoginRequest; - try { - const bodyText = await req.text(); - body = JSON.parse(bodyText); - } catch (parseError) { - return new NextResponse(JSON.stringify({ - error: 'Invalid JSON in request body' - }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const { username, password } = body; - - // Validate input - if (!username || !password) { - return new NextResponse(JSON.stringify({ - error: 'Username and password are required' - }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // Get user by username or email - const userResult = await client.query(` - SELECT id, username, email, password_hash, first_name, last_name, is_active, last_login - FROM users - WHERE (username = $1 OR email = $1) AND is_active = true - `, [username]); - - if (userResult.rows.length === 0) { - return new NextResponse(JSON.stringify({ - error: 'Invalid credentials' - }), { - status: 401, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const user = userResult.rows[0]; - - // Verify password - const isValidPassword = await bcrypt.compare(password, user.password_hash); - if (!isValidPassword) { - return new NextResponse(JSON.stringify({ - error: 'Invalid credentials' - }), { - status: 401, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // Get user roles and permissions - const userRoles = await client.query(` - SELECT r.name, r.description, - array_agg(p.name) as permissions - FROM roles r - JOIN user_roles ur ON r.id = ur.role_id - LEFT JOIN role_permissions rp ON r.id = rp.role_id - LEFT JOIN permissions p ON rp.permission_id = p.id - WHERE ur.user_id = $1 - GROUP BY r.id, r.name, r.description - `, [user.id]); - - const roles = userRoles.rows.map(r => r.name); - const permissions = [...new Set(userRoles.rows.flatMap(r => r.permissions || []))]; - - // Generate JWT token - const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production'; - const token = jwt.sign( - { - userId: user.id, - username: user.username, - email: user.email, - roles, - permissions - }, - jwtSecret, - { expiresIn: '7d' } - ); - - // Store session - const tokenHash = await bcrypt.hash(token, 10); - const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days - - await client.query(` - INSERT INTO user_sessions (user_id, token_hash, expires_at, user_agent, ip_address) - VALUES ($1, $2, $3, $4, $5) - `, [ - user.id, - tokenHash, - expiresAt, - req.headers.get('user-agent') || null, - req.headers.get('x-forwarded-for') || req.headers.get('x-real-ip') || null - ]); - - // Update last login - await client.query( - 'UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = $1', - [user.id] - ); - - return new NextResponse(JSON.stringify({ - success: true, - message: 'Login successful', - user: { - id: user.id, - username: user.username, - email: user.email, - firstName: user.first_name, - lastName: user.last_name, - roles, - permissions, - lastLogin: user.last_login - }, - token - }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - - } catch (error) { - console.error('Login error:', error); - return new NextResponse(JSON.stringify({ - error: 'Login failed', - details: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined - }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } finally { - client.release(); - } -} \ No newline at end of file diff --git a/api/auth/register.ts b/api/auth/register.ts deleted file mode 100644 index 365896c..0000000 --- a/api/auth/register.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { Pool } from 'pg'; -import bcrypt from 'bcryptjs'; -import jwt from 'jsonwebtoken'; - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, -}); - -interface RegisterRequest { - username: string; - email: string; - password: string; - firstName?: string; - lastName?: string; -} - -export default async function handler(req: NextRequest) { - if (req.method !== 'POST') { - return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), { - status: 405, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // 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 { - // Parse request body - let body: RegisterRequest; - try { - const bodyText = await req.text(); - body = JSON.parse(bodyText); - } catch (parseError) { - return new NextResponse(JSON.stringify({ - error: 'Invalid JSON in request body' - }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const { username, email, password, firstName, lastName } = body; - - // Validate input - if (!username || !email || !password) { - return new NextResponse(JSON.stringify({ - error: 'Username, email, and password are required' - }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - if (password.length < 6) { - return new NextResponse(JSON.stringify({ - error: 'Password must be at least 6 characters long' - }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // 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', - [username, email] - ); - - if (existingUser.rows.length > 0) { - return new NextResponse(JSON.stringify({ - error: 'Username or email already exists' - }), { - status: 409, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // Hash password - const saltRounds = 12; - const passwordHash = await bcrypt.hash(password, saltRounds); - - // Create user - const userResult = await client.query(` - INSERT INTO users (username, email, password_hash, first_name, last_name) - VALUES ($1, $2, $3, $4, $5) - RETURNING id, username, email, first_name, last_name, created_at - `, [username, email, passwordHash, firstName || null, lastName || null]); - - const user = userResult.rows[0]; - - // Assign default 'user' role - const roleResult = await client.query( - 'SELECT id FROM roles WHERE name = $1', - ['user'] - ); - - if (roleResult.rows.length > 0) { - await client.query( - 'INSERT INTO user_roles (user_id, role_id) VALUES ($1, $2)', - [user.id, roleResult.rows[0].id] - ); - } - - // Generate JWT token - const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production'; - const token = jwt.sign( - { - userId: user.id, - username: user.username, - email: user.email - }, - jwtSecret, - { expiresIn: '7d' } - ); - - // Store session - const tokenHash = await bcrypt.hash(token, 10); - const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days - - await client.query(` - INSERT INTO user_sessions (user_id, token_hash, expires_at, user_agent, ip_address) - VALUES ($1, $2, $3, $4, $5) - `, [ - user.id, - tokenHash, - expiresAt, - req.headers.get('user-agent') || null, - req.headers.get('x-forwarded-for') || req.headers.get('x-real-ip') || null - ]); - - // Get user roles for response - const userRoles = await client.query(` - SELECT r.name, r.description - FROM roles r - JOIN user_roles ur ON r.id = ur.role_id - WHERE ur.user_id = $1 - `, [user.id]); - - return new NextResponse(JSON.stringify({ - success: true, - message: 'User registered successfully', - user: { - id: user.id, - username: user.username, - email: user.email, - firstName: user.first_name, - lastName: user.last_name, - roles: userRoles.rows.map(r => r.name), - createdAt: user.created_at - }, - token - }), { - status: 201, - headers: { 'Content-Type': 'application/json' }, - }); - - } catch (error) { - console.error('Registration error:', error); - return new NextResponse(JSON.stringify({ - error: 'Registration failed', - details: process.env.NODE_ENV === 'development' ? (error as Error).message : undefined - }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } finally { - client.release(); - } -} \ No newline at end of file diff --git a/api/health.ts b/api/health.ts deleted file mode 100644 index 1e21770..0000000 --- a/api/health.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; - -export default async function handler(req: NextRequest) { - return new NextResponse(JSON.stringify({ - success: true, - message: 'API is working!', - timestamp: new Date().toISOString(), - environment: process.env.NODE_ENV || 'unknown', - has_database_url: !!process.env.DATABASE_URL, - has_jwt_secret: !!process.env.JWT_SECRET, - method: req.method, - url: req.url - }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/api/hello.ts b/api/hello.ts deleted file mode 100644 index 3baa498..0000000 --- a/api/hello.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; - -type Data = { - message: string; - timestamp: string; - environment: string; - has_database_url: boolean; -} - -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { - res.status(200).json({ - message: 'Hello from TCG Vault API!', - timestamp: new Date().toISOString(), - environment: process.env.NODE_ENV || 'unknown', - has_database_url: !!process.env.DATABASE_URL - }); -} \ No newline at end of file diff --git a/api/setup-auth.ts b/api/setup-auth.ts deleted file mode 100644 index 019de07..0000000 --- a/api/setup-auth.ts +++ /dev/null @@ -1,185 +0,0 @@ -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 !== 'POST') { - return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), { - status: 405, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const client = await pool.connect(); - - try { - console.log('Setting up user authentication schema...'); - - // 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(` - CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(100) UNIQUE NOT NULL, - password_hash VARCHAR(255) NOT NULL, - first_name VARCHAR(50), - last_name VARCHAR(50), - avatar_url TEXT, - is_active BOOLEAN DEFAULT true, - email_verified BOOLEAN DEFAULT false, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - last_login TIMESTAMP WITH TIME ZONE - ); - `); - - 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 - ); - `); - - await client.query(` - CREATE TABLE IF NOT EXISTS user_roles ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, - role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE, - assigned_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - assigned_by INTEGER REFERENCES users(id), - UNIQUE(user_id, role_id) - ); - `); - - await client.query(` - CREATE TABLE IF NOT EXISTS permissions ( - id SERIAL PRIMARY KEY, - name VARCHAR(100) UNIQUE NOT NULL, - description TEXT, - resource VARCHAR(50), - action VARCHAR(50), - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP - ); - `); - - 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) - ); - `); - - await client.query(` - CREATE TABLE IF NOT EXISTS user_sessions ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, - token_hash VARCHAR(255) NOT NULL, - expires_at TIMESTAMP WITH TIME ZONE NOT NULL, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - last_used TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - user_agent TEXT, - ip_address INET - ); - `); - - // Insert default roles - await client.query(` - INSERT INTO roles (name, description) VALUES - ('user', 'Standard user with basic permissions'), - ('admin', 'Administrator with full system access') - ON CONFLICT (name) DO NOTHING; - `); - - // Insert basic permissions - const permissions = [ - ['cards.read', 'View cards'], - ['collections.manage', 'Manage collections'], - ['decks.manage', 'Manage decks'], - ['admin.access', 'Access admin panel'] - ]; - - for (const [name, description] of permissions) { - await client.query(` - INSERT INTO permissions (name, description) - VALUES ($1, $2) - ON CONFLICT (name) DO NOTHING; - `, [name, description]); - } - - // Assign permissions to roles - 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.manage', 'decks.manage') - ON CONFLICT (role_id, permission_id) DO NOTHING; - `); - - await client.query(` - INSERT INTO role_permissions (role_id, permission_id) - SELECT r.id, p.id - FROM roles r, permissions p - WHERE r.name = 'admin' - ON CONFLICT (role_id, permission_id) DO NOTHING; - `); - - // 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); - `); - - console.log('✅ User authentication schema setup completed!'); - - return new NextResponse(JSON.stringify({ - success: true, - message: 'User authentication schema setup completed successfully' - }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - - } catch (error) { - console.error('Schema setup failed:', error); - return new NextResponse(JSON.stringify({ - success: false, - error: 'Schema setup failed', - details: (error as Error).message - }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } finally { - client.release(); - } -} \ No newline at end of file diff --git a/api/simple.ts b/api/simple.ts deleted file mode 100644 index 046d5fe..0000000 --- a/api/simple.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default function handler(req: any, res: any) { - res.status(200).json({ - message: 'Simple endpoint works!', - timestamp: new Date().toISOString() - }); -} \ No newline at end of file diff --git a/api/test-db.ts b/api/test-db.ts deleted file mode 100644 index ae3bc9c..0000000 --- a/api/test-db.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { Pool } from 'pg'; - -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' }, - }); - } - - // Check environment variables first - if (!process.env.DATABASE_URL) { - return new NextResponse(JSON.stringify({ - success: false, - error: 'DATABASE_URL environment variable not set', - environment: process.env.NODE_ENV || 'unknown' - }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } - - const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, - connectionTimeoutMillis: 10000, // 10 seconds - query_timeout: 10000, // 10 seconds - }); - - let client: any; - - try { - console.log('Testing database connection...'); - - // Set a timeout for the entire operation - const timeoutPromise = new Promise((_, reject) => { - setTimeout(() => reject(new Error('Database operation timed out')), 15000); - }); - - const dbOperation = async () => { - client = await pool.connect(); - - // 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; - `); - - return { - 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 - }; - }; - - const dbResult = await Promise.race([dbOperation(), timeoutPromise]); - - return new NextResponse(JSON.stringify({ - success: true, - message: 'Database connection successful', - database_info: dbResult - }), { - 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, - database_url_exists: !!process.env.DATABASE_URL, - database_url_preview: process.env.DATABASE_URL ? - process.env.DATABASE_URL.substring(0, 20) + '...' : 'not set' - }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } finally { - if (client) { - try { - client.release(); - } catch (e) { - console.error('Error releasing client:', e); - } - } - try { - await pool.end(); - } catch (e) { - console.error('Error ending pool:', e); - } - } -} \ No newline at end of file