mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 18:26:01 +00:00
- Create post-merge git hook for auto service restart - Add setup-auto-deploy.sh for easy installation - Hook detects PM2, systemd, or manual process management - Automatically runs npm install if package.json changes - Eliminates need to manually run deploy.sh after updates
269 lines
11 KiB
JavaScript
269 lines
11 KiB
JavaScript
// Comprehensive PostgreSQL Database Verification Script
|
|
// Run with: node verify-database.js
|
|
|
|
const { Pool } = require('pg');
|
|
const dbConfig = require('./db.config.js');
|
|
|
|
const pool = new Pool({
|
|
host: dbConfig.HOST,
|
|
user: dbConfig.USER,
|
|
password: dbConfig.PASSWORD,
|
|
database: dbConfig.DB,
|
|
port: 5432
|
|
});
|
|
|
|
async function verifyDatabase() {
|
|
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
console.log('║ PostgreSQL Database Verification Report ║');
|
|
console.log('╚════════════════════════════════════════════════════════════╝');
|
|
console.log('');
|
|
|
|
try {
|
|
// 1. Test Connection
|
|
console.log('📡 Testing Database Connection...');
|
|
const startTime = Date.now();
|
|
await pool.query('SELECT 1');
|
|
const latency = Date.now() - startTime;
|
|
console.log(`✅ Connection successful (${latency}ms latency)`);
|
|
console.log(` Host: ${dbConfig.HOST}`);
|
|
console.log(` Database: ${dbConfig.DB}`);
|
|
console.log('');
|
|
|
|
// 2. Check Tables
|
|
console.log('📋 Checking Tables...');
|
|
const tablesQuery = `
|
|
SELECT table_name,
|
|
(SELECT COUNT(*) FROM information_schema.columns
|
|
WHERE table_schema = 'public' AND table_name = t.table_name) as column_count
|
|
FROM information_schema.tables t
|
|
WHERE table_schema = 'public'
|
|
AND table_name IN ('players', 'active_sessions', 'games', 'game_moves')
|
|
ORDER BY table_name;
|
|
`;
|
|
const tablesResult = await pool.query(tablesQuery);
|
|
|
|
if (tablesResult.rows.length === 4) {
|
|
console.log('✅ All 4 required tables exist:');
|
|
tablesResult.rows.forEach(row => {
|
|
console.log(` - ${row.table_name} (${row.column_count} columns)`);
|
|
});
|
|
} else {
|
|
console.log(`❌ Missing tables! Found ${tablesResult.rows.length}/4`);
|
|
const foundTables = tablesResult.rows.map(r => r.table_name);
|
|
const requiredTables = ['players', 'active_sessions', 'games', 'game_moves'];
|
|
const missingTables = requiredTables.filter(t => !foundTables.includes(t));
|
|
if (missingTables.length > 0) {
|
|
console.log(` Missing: ${missingTables.join(', ')}`);
|
|
}
|
|
}
|
|
console.log('');
|
|
|
|
// 3. Check Table Structures
|
|
console.log('🏗️ Verifying Table Structures...');
|
|
|
|
// Players table
|
|
const playersColumns = await pool.query(`
|
|
SELECT column_name, data_type, is_nullable, column_default
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public' AND table_name = 'players'
|
|
ORDER BY ordinal_position;
|
|
`);
|
|
console.log(`✅ players table (${playersColumns.rows.length} columns):`);
|
|
playersColumns.rows.forEach(col => {
|
|
console.log(` - ${col.column_name}: ${col.data_type} ${col.is_nullable === 'NO' ? 'NOT NULL' : 'NULL'}`);
|
|
});
|
|
console.log('');
|
|
|
|
// Active sessions table
|
|
const sessionsColumns = await pool.query(`
|
|
SELECT column_name, data_type
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public' AND table_name = 'active_sessions'
|
|
ORDER BY ordinal_position;
|
|
`);
|
|
console.log(`✅ active_sessions table (${sessionsColumns.rows.length} columns):`);
|
|
sessionsColumns.rows.forEach(col => {
|
|
console.log(` - ${col.column_name}: ${col.data_type}`);
|
|
});
|
|
console.log('');
|
|
|
|
// Games table
|
|
const gamesColumns = await pool.query(`
|
|
SELECT column_name, data_type
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public' AND table_name = 'games'
|
|
ORDER BY ordinal_position;
|
|
`);
|
|
console.log(`✅ games table (${gamesColumns.rows.length} columns):`);
|
|
gamesColumns.rows.forEach(col => {
|
|
console.log(` - ${col.column_name}: ${col.data_type}`);
|
|
});
|
|
console.log('');
|
|
|
|
// Game moves table
|
|
const movesColumns = await pool.query(`
|
|
SELECT column_name, data_type
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public' AND table_name = 'game_moves'
|
|
ORDER BY ordinal_position;
|
|
`);
|
|
console.log(`✅ game_moves table (${movesColumns.rows.length} columns):`);
|
|
movesColumns.rows.forEach(col => {
|
|
console.log(` - ${col.column_name}: ${col.data_type}`);
|
|
});
|
|
console.log('');
|
|
|
|
// 4. Check Indexes
|
|
console.log('🔍 Checking Indexes...');
|
|
const indexesQuery = `
|
|
SELECT
|
|
indexname,
|
|
tablename
|
|
FROM pg_indexes
|
|
WHERE schemaname = 'public'
|
|
AND tablename IN ('players', 'active_sessions', 'games', 'game_moves')
|
|
ORDER BY tablename, indexname;
|
|
`;
|
|
const indexesResult = await pool.query(indexesQuery);
|
|
console.log(`✅ Found ${indexesResult.rows.length} indexes:`);
|
|
indexesResult.rows.forEach(idx => {
|
|
console.log(` - ${idx.indexname} on ${idx.tablename}`);
|
|
});
|
|
console.log('');
|
|
|
|
// 5. Check Functions
|
|
console.log('⚙️ Checking Functions...');
|
|
const functionsQuery = `
|
|
SELECT routine_name
|
|
FROM information_schema.routines
|
|
WHERE routine_schema = 'public'
|
|
AND routine_name IN ('increment_wins', 'increment_losses', 'increment_draws')
|
|
ORDER BY routine_name;
|
|
`;
|
|
const functionsResult = await pool.query(functionsQuery);
|
|
if (functionsResult.rows.length === 3) {
|
|
console.log('✅ All 3 required functions exist:');
|
|
functionsResult.rows.forEach(func => {
|
|
console.log(` - ${func.routine_name}()`);
|
|
});
|
|
} else {
|
|
console.log(`⚠️ Found ${functionsResult.rows.length}/3 functions`);
|
|
functionsResult.rows.forEach(func => {
|
|
console.log(` - ${func.routine_name}()`);
|
|
});
|
|
}
|
|
console.log('');
|
|
|
|
// 6. Check Enum Type
|
|
console.log('📊 Checking Custom Types...');
|
|
const enumQuery = `
|
|
SELECT typname, enumlabel
|
|
FROM pg_type t
|
|
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
WHERE typname = 'game_state_enum'
|
|
ORDER BY enumsortorder;
|
|
`;
|
|
const enumResult = await pool.query(enumQuery);
|
|
if (enumResult.rows.length > 0) {
|
|
console.log('✅ game_state_enum type exists with values:');
|
|
enumResult.rows.forEach(e => {
|
|
console.log(` - ${e.enumlabel}`);
|
|
});
|
|
} else {
|
|
console.log('❌ game_state_enum type not found');
|
|
}
|
|
console.log('');
|
|
|
|
// 7. Check Data
|
|
console.log('📈 Checking Data...');
|
|
|
|
const playersCount = await pool.query('SELECT COUNT(*) FROM players');
|
|
console.log(` Players: ${playersCount.rows[0].count} records`);
|
|
|
|
const sessionsCount = await pool.query('SELECT COUNT(*) FROM active_sessions');
|
|
console.log(` Active Sessions: ${sessionsCount.rows[0].count} records`);
|
|
|
|
const gamesCount = await pool.query('SELECT COUNT(*) FROM games');
|
|
console.log(` Games: ${gamesCount.rows[0].count} records`);
|
|
|
|
const movesCount = await pool.query('SELECT COUNT(*) FROM game_moves');
|
|
console.log(` Game Moves: ${movesCount.rows[0].count} records`);
|
|
console.log('');
|
|
|
|
// 8. Sample some player data if exists
|
|
if (parseInt(playersCount.rows[0].count) > 0) {
|
|
console.log('👤 Recent Players (last 5):');
|
|
const recentPlayers = await pool.query(`
|
|
SELECT username, total_wins, total_losses, total_draws,
|
|
TO_CHAR(created_at, 'YYYY-MM-DD HH24:MI:SS') as created
|
|
FROM players
|
|
ORDER BY created_at DESC
|
|
LIMIT 5;
|
|
`);
|
|
recentPlayers.rows.forEach(p => {
|
|
console.log(` - ${p.username} (W:${p.total_wins} L:${p.total_losses} D:${p.total_draws}) - Created: ${p.created}`);
|
|
});
|
|
console.log('');
|
|
}
|
|
|
|
// 9. Test Write Capability
|
|
console.log('✏️ Testing Write Capability...');
|
|
const testUsername = `_verify_test_${Date.now()}`;
|
|
|
|
// Insert test record
|
|
const insertResult = await pool.query(
|
|
'INSERT INTO players (username) VALUES ($1) RETURNING id',
|
|
[testUsername]
|
|
);
|
|
const testId = insertResult.rows[0].id;
|
|
console.log(`✅ Write test: Created test player (ID: ${testId})`);
|
|
|
|
// Read it back
|
|
const readResult = await pool.query(
|
|
'SELECT * FROM players WHERE id = $1',
|
|
[testId]
|
|
);
|
|
if (readResult.rows.length > 0) {
|
|
console.log('✅ Read test: Successfully retrieved test player');
|
|
}
|
|
|
|
// Update it
|
|
await pool.query(
|
|
'UPDATE players SET total_wins = 1 WHERE id = $1',
|
|
[testId]
|
|
);
|
|
console.log('✅ Update test: Successfully updated test player');
|
|
|
|
// Delete it
|
|
await pool.query('DELETE FROM players WHERE id = $1', [testId]);
|
|
console.log('✅ Delete test: Successfully deleted test player');
|
|
console.log('');
|
|
|
|
// Final Summary
|
|
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
console.log('║ VERIFICATION SUMMARY ║');
|
|
console.log('╚════════════════════════════════════════════════════════════╝');
|
|
console.log('');
|
|
console.log('✅ Database Connection: PASSED');
|
|
console.log('✅ Tables (4/4): PASSED');
|
|
console.log('✅ Table Structures: VERIFIED');
|
|
console.log('✅ Indexes: PRESENT');
|
|
console.log('✅ Functions (3/3): PASSED');
|
|
console.log('✅ Enum Types: PRESENT');
|
|
console.log('✅ Write/Read/Update/Delete: PASSED');
|
|
console.log('');
|
|
console.log('🎉 Database deployment is FULLY FUNCTIONAL!');
|
|
console.log('');
|
|
|
|
} catch (error) {
|
|
console.error('❌ Verification failed:', error.message);
|
|
console.error('');
|
|
console.error('Error details:', error);
|
|
process.exit(1);
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
verifyDatabase();
|