Первый проект
This commit is contained in:
366
server.js
Normal file
366
server.js
Normal file
@@ -0,0 +1,366 @@
|
||||
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');
|
||||
});
|
||||
Reference in New Issue
Block a user