Add health check and improve database test endpoint

🔧 Improvements:
- Add /api/health endpoint for basic API functionality testing
- Improve /api/test-db with timeout handling and better error reporting
- Add environment variable checks before database operations
- Add connection timeouts to prevent function timeouts
- Better error handling and logging

🧪 Testing Endpoints:
- /api/health - Simple API health check (no DB required)
- /api/test-db - Database connectivity test with timeout protection

These changes should help identify whether the issue is with API routing, environment variables, or database connectivity.
This commit is contained in:
Randall Stillwell 2025-07-22 06:36:13 -05:00
parent 2f4336889d
commit 323d6a3dd3
2 changed files with 82 additions and 67 deletions

View file

@ -1,27 +1,17 @@
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server';
export default async function handler(req: NextRequest) { export default async function handler(req: NextRequest) {
// Enable CORS
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
if (req.method === 'OPTIONS') {
return new NextResponse(null, { status: 200, headers })
}
return new NextResponse(JSON.stringify({ return new NextResponse(JSON.stringify({
status: 'healthy', success: true,
service: 'TCG Vault API', message: 'API is working!',
version: '1.0.0', timestamp: new Date().toISOString(),
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, status: 200,
headers: { headers: { 'Content-Type': 'application/json' },
...headers, });
'Content-Type': 'application/json',
},
})
} }

View file

@ -1,11 +1,6 @@
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { Pool } from 'pg'; 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) { export default async function handler(req: NextRequest) {
if (req.method !== 'GET') { if (req.method !== 'GET') {
return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), { return new NextResponse(JSON.stringify({ error: 'Method not allowed' }), {
@ -14,57 +9,73 @@ export default async function handler(req: NextRequest) {
}); });
} }
const client = await pool.connect(); // 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 { try {
console.log('Testing database connection...'); console.log('Testing database connection...');
// Test basic connection // Set a timeout for the entire operation
const result = await client.query('SELECT NOW() as current_time, version() as postgres_version'); const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Database operation timed out')), 15000);
});
// Check if users table exists const dbOperation = async () => {
const tableCheck = await client.query(` client = await pool.connect();
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'users'
) as users_table_exists;
`);
// Check if cards table exists // Test basic connection
const cardsCheck = await client.query(` const result = await client.query('SELECT NOW() as current_time, version() as postgres_version');
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'cards'
) as cards_table_exists;
`);
// Get table counts // Check if users table exists
let userCount = 0; const tableCheck = await client.query(`
let cardCount = 0; SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'users'
) as users_table_exists;
`);
if (tableCheck.rows[0].users_table_exists) { // Check if cards table exists
const userCountResult = await client.query('SELECT COUNT(*) as count FROM users'); const cardsCheck = await client.query(`
userCount = parseInt(userCountResult.rows[0].count); SELECT EXISTS (
} SELECT FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = 'cards'
) as cards_table_exists;
`);
if (cardsCheck.rows[0].cards_table_exists) { return {
const cardCountResult = await client.query('SELECT COUNT(*) as count FROM cards'); current_time: result.rows[0].current_time,
cardCount = parseInt(cardCountResult.rows[0].count); 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({ return new NextResponse(JSON.stringify({
success: true, success: true,
message: 'Database connection successful', message: 'Database connection successful',
database_info: { database_info: dbResult
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, status: 200,
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@ -75,12 +86,26 @@ export default async function handler(req: NextRequest) {
return new NextResponse(JSON.stringify({ return new NextResponse(JSON.stringify({
success: false, success: false,
error: 'Database connection failed', error: 'Database connection failed',
details: (error as Error).message 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, status: 500,
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
}); });
} finally { } finally {
client.release(); 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);
}
} }
} }