require('dotenv').config(); const express = require('express'); const { Pool } = require('pg'); const session = require('express-session'); const cors = require('cors'); const path = require('path'); const app = express(); // Middleware app.use(cors()); app.use(express.json()); app.use(express.static('.')); // Session configuration app.use(session({ secret: process.env.SESSION_SECRET || 'default-secret-change-this', resave: false, saveUninitialized: false, cookie: { secure: false } // Set to true if using HTTPS })); // Database connection pool (uses .env configuration) const pool = new Pool({ host: process.env.DB_HOST, port: process.env.DB_PORT, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, }); // Test database connection on startup pool.connect((err, client, release) => { if (err) { console.error('❌ Error connecting to PostgreSQL:', err.message); console.log('Проверьте настройки в .env файле'); } else { console.log('✅ Connected to PostgreSQL database'); console.log(` Host: ${process.env.DB_HOST}:${process.env.DB_PORT}`); console.log(` Database: ${process.env.DB_NAME}`); release(); } }); // Middleware to check if user is authenticated const requireAuth = (req, res, next) => { if (req.session && req.session.authenticated) { next(); } else { res.status(401).json({ error: 'Unauthorized' }); } }; // Login endpoint - checks admin credentials from .env app.post('/api/login', async (req, res) => { const { username, password } = req.body; // Check against .env credentials if (username === process.env.ADMIN_USERNAME && password === process.env.ADMIN_PASSWORD) { // Test database connection try { const client = await pool.connect(); const result = await client.query('SELECT NOW() as time'); client.release(); req.session.authenticated = true; req.session.username = username; res.json({ success: true, message: 'Login successful', dbInfo: { host: process.env.DB_HOST, port: process.env.DB_PORT, database: process.env.DB_NAME, connected: true, serverTime: result.rows[0].time } }); } catch (err) { res.status(500).json({ success: false, error: 'Database connection failed', details: err.message }); } } else { res.status(401).json({ success: false, error: 'Invalid credentials' }); } }); // Logout app.post('/api/logout', (req, res) => { req.session.destroy(); res.json({ success: true }); }); // Check session app.get('/api/session', (req, res) => { if (req.session.authenticated) { res.json({ authenticated: true, username: req.session.username, dbInfo: { host: process.env.DB_HOST, port: process.env.DB_PORT, database: process.env.DB_NAME } }); } else { res.json({ authenticated: false }); } }); // Get all tables app.get('/api/tables', requireAuth, async (req, res) => { try { const result = await pool.query(` SELECT table_name as name, (SELECT COUNT(*) FROM information_schema.columns WHERE table_name = t.table_name) as column_count FROM information_schema.tables t WHERE table_schema = 'public' ORDER BY table_name `); // Get row counts for each table const tablesWithCounts = await Promise.all( result.rows.map(async (table) => { try { const countResult = await pool.query(`SELECT COUNT(*) as count FROM "${table.name}"`); return { ...table, rows: parseInt(countResult.rows[0].count), size: 'calculating...' // Would need pg_size_pretty for real size }; } catch (e) { return { ...table, rows: 0, size: '0 KB' }; } }) ); res.json(tablesWithCounts); } catch (err) { res.status(500).json({ error: err.message }); } }); // Get table data with pagination app.get('/api/tables/:tableName/data', requireAuth, async (req, res) => { const { tableName } = req.params; const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 10; const offset = (page - 1) * limit; try { // Get total count const countResult = await pool.query(`SELECT COUNT(*) as total FROM "${tableName}"`); const total = parseInt(countResult.rows[0].total); // Get data const result = await pool.query(` SELECT * FROM "${tableName}" LIMIT $1 OFFSET $2 `, [limit, offset]); res.json({ data: result.rows, total, page, limit, totalPages: Math.ceil(total / limit) }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Get table structure app.get('/api/tables/:tableName/structure', requireAuth, async (req, res) => { const { tableName } = req.params; try { const result = await pool.query(` SELECT column_name as name, data_type as type, is_nullable as nullable, column_default as default_value FROM information_schema.columns WHERE table_name = $1 AND table_schema = 'public' ORDER BY ordinal_position `, [tableName]); res.json(result.rows); } catch (err) { res.status(500).json({ error: err.message }); } }); // Create table app.post('/api/tables', requireAuth, async (req, res) => { const { name, columns } = req.body; try { let columnsSQL = columns.map(col => { let def = `"${col.name}" ${col.type}`; if (col.pk) def += ' PRIMARY KEY'; if (!col.nullable && !col.pk) def += ' NOT NULL'; return def; }).join(', '); const sql = `CREATE TABLE "${name}" (${columnsSQL})`; await pool.query(sql); res.json({ success: true, message: 'Table created' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Delete table app.delete('/api/tables/:tableName', requireAuth, async (req, res) => { const { tableName } = req.params; try { await pool.query(`DROP TABLE IF EXISTS "${tableName}"`); res.json({ success: true, message: 'Table deleted' }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Insert record app.post('/api/tables/:tableName/records', requireAuth, async (req, res) => { const { tableName } = req.params; const data = req.body; const columns = Object.keys(data); const values = Object.values(data); const placeholders = values.map((_, i) => `$${i + 1}`).join(', '); try { const sql = `INSERT INTO "${tableName}" (${columns.map(c => `"${c}"`).join(', ')}) VALUES (${placeholders}) RETURNING *`; const result = await pool.query(sql, values); res.json({ success: true, data: result.rows[0] }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Update record app.put('/api/tables/:tableName/records/:id', requireAuth, async (req, res) => { const { tableName, id } = req.params; const data = req.body; const columns = Object.keys(data); const values = Object.values(data); const setClause = columns.map((col, i) => `"${col}" = $${i + 1}`).join(', '); try { // Assuming id column is named 'id' - in production you'd need to detect PK const sql = `UPDATE "${tableName}" SET ${setClause} WHERE id = $${values.length + 1} RETURNING *`; const result = await pool.query(sql, [...values, id]); res.json({ success: true, data: result.rows[0] }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Delete record app.delete('/api/tables/:tableName/records/:id', requireAuth, async (req, res) => { const { tableName, id } = req.params; try { await pool.query(`DELETE FROM "${tableName}" WHERE id = $1`, [id]); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Execute SQL app.post('/api/query', requireAuth, async (req, res) => { const { sql } = req.body; try { const result = await pool.query(sql); res.json({ success: true, rows: result.rows, rowCount: result.rowCount, command: result.command }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Get indexes app.get('/api/tables/:tableName/indexes', requireAuth, async (req, res) => { const { tableName } = req.params; try { const result = await pool.query(` SELECT indexname as name, indexdef as definition FROM pg_indexes WHERE tablename = $1 `, [tableName]); const indexes = result.rows.map(row => ({ name: row.name, columns: row.definition.match(/\((.*?)\)/)?.[1] || 'unknown', unique: row.definition.includes('UNIQUE'), type: 'btree' })); res.json(indexes); } catch (err) { res.status(500).json({ error: err.message }); } }); // Create index app.post('/api/tables/:tableName/indexes', requireAuth, async (req, res) => { const { tableName } = req.params; const { name, columns, unique } = req.body; try { const uniqueStr = unique ? 'UNIQUE' : ''; const sql = `CREATE ${uniqueStr} INDEX "${name}" ON "${tableName}" (${columns})`; await pool.query(sql); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Drop index app.delete('/api/indexes/:indexName', requireAuth, async (req, res) => { const { indexName } = req.params; try { await pool.query(`DROP INDEX IF EXISTS "${indexName}"`); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Start server const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`🚀 Server running on http://localhost:${PORT}`); console.log(''); console.log('🔑 Default login credentials:'); console.log(` Username: ${process.env.ADMIN_USERNAME || 'admin'}`); console.log(` Password: ${process.env.ADMIN_PASSWORD || 'admin'}`); console.log(''); console.log('📝 Make sure to configure your database in .env file'); });