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) {
// 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({
status: 'healthy',
service: 'TCG Vault API',
version: '1.0.0',
timestamp: new Date().toISOString()
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: {
...headers,
'Content-Type': 'application/json',
},
})
headers: { 'Content-Type': 'application/json' },
});
}

View file

@ -1,11 +1,6 @@
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' }), {
@ -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 {
console.log('Testing database connection...');
// Test basic connection
const result = await client.query('SELECT NOW() as current_time, version() as postgres_version');
// Set a timeout for the entire operation
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Database operation timed out')), 15000);
});
// 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;
`);
const dbOperation = async () => {
client = await pool.connect();
// 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;
`);
// Test basic connection
const result = await client.query('SELECT NOW() as current_time, version() as postgres_version');
// Get table counts
let userCount = 0;
let cardCount = 0;
// 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;
`);
if (tableCheck.rows[0].users_table_exists) {
const userCountResult = await client.query('SELECT COUNT(*) as count FROM users');
userCount = parseInt(userCountResult.rows[0].count);
}
// 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;
`);
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 {
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: {
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
}
database_info: dbResult
}), {
status: 200,
headers: { 'Content-Type': 'application/json' },
@ -75,12 +86,26 @@ export default async function handler(req: NextRequest) {
return new NextResponse(JSON.stringify({
success: false,
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,
headers: { 'Content-Type': 'application/json' },
});
} 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);
}
}
}