adminka
This commit is contained in:
158
server.js
158
server.js
@@ -4,6 +4,44 @@ const { Pool } = require('pg');
|
||||
const session = require('express-session');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
let usersConfig = { users: [] };
|
||||
try {
|
||||
usersConfig = JSON.parse(fs.readFileSync(path.join(__dirname, 'users.json'), 'utf8'));
|
||||
} catch (err) {
|
||||
console.warn('⚠️ users.json not found or invalid JSON. Falling back to env-based admin only.');
|
||||
}
|
||||
|
||||
const rolePermissions = {
|
||||
superadmin: { folders: null, canCreate: true, canEdit: true, canDelete: true },
|
||||
frontend_admin: { folders: ['frontend'], canCreate: true, canEdit: true, canDelete: true },
|
||||
backend_admin: { folders: ['backend'], canCreate: true, canEdit: true, canDelete: true },
|
||||
frontend_moder: { folders: ['frontend'], canCreate: true, canEdit: true, canDelete: false },
|
||||
backend_moder: { folders: ['backend'], canCreate: true, canEdit: true, canDelete: false },
|
||||
viewer: { folders: null, canCreate: false, canEdit: false, canDelete: false },
|
||||
};
|
||||
|
||||
function getUser(username) {
|
||||
return usersConfig.users.find(u => u.username === username);
|
||||
}
|
||||
|
||||
function getTableFolder(tableName) {
|
||||
if (!tableName) return 'default';
|
||||
const parts = tableName.split('__');
|
||||
return parts.length > 1 ? parts[0] : 'default';
|
||||
}
|
||||
|
||||
function getRolePermissions(role) {
|
||||
return rolePermissions[role] || rolePermissions.viewer;
|
||||
}
|
||||
|
||||
function canAccessTable(role, tableName) {
|
||||
const perms = getRolePermissions(role);
|
||||
if (!perms.folders) return true;
|
||||
const folder = getTableFolder(tableName);
|
||||
return perms.folders.includes(folder);
|
||||
}
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -68,24 +106,26 @@ const requireAuth = (req, res, next) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Login endpoint - checks admin credentials from .env
|
||||
// Login endpoint - checks users.json (fallback to .env admin)
|
||||
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 users.json first
|
||||
const user = getUser(username);
|
||||
if (user && password === user.password) {
|
||||
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;
|
||||
|
||||
req.session.role = user.role;
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Login successful',
|
||||
role: user.role,
|
||||
dbInfo: {
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
@@ -94,19 +134,55 @@ app.post('/api/login', async (req, res) => {
|
||||
serverTime: result.rows[0].time
|
||||
}
|
||||
});
|
||||
return;
|
||||
} catch (err) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Database connection failed',
|
||||
details: err.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Fallback to env-based admin
|
||||
if (username === process.env.ADMIN_USERNAME && password === process.env.ADMIN_PASSWORD) {
|
||||
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;
|
||||
req.session.role = 'superadmin';
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Login successful',
|
||||
role: 'superadmin',
|
||||
dbInfo: {
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
database: process.env.DB_NAME,
|
||||
connected: true,
|
||||
serverTime: result.rows[0].time
|
||||
}
|
||||
});
|
||||
return;
|
||||
} catch (err) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Database connection failed',
|
||||
details: err.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
});
|
||||
|
||||
// Logout
|
||||
@@ -121,6 +197,8 @@ app.get('/api/session', (req, res) => {
|
||||
res.json({
|
||||
authenticated: true,
|
||||
username: req.session.username,
|
||||
role: req.session.role || 'viewer',
|
||||
permissions: getRolePermissions(req.session.role),
|
||||
dbInfo: {
|
||||
host: process.env.DB_HOST,
|
||||
port: process.env.DB_PORT,
|
||||
@@ -135,6 +213,7 @@ app.get('/api/session', (req, res) => {
|
||||
// Get all tables
|
||||
app.get('/api/tables', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const role = req.session.role || 'viewer';
|
||||
const result = await pool.query(`
|
||||
SELECT
|
||||
table_name as name,
|
||||
@@ -143,10 +222,13 @@ app.get('/api/tables', requireAuth, async (req, res) => {
|
||||
WHERE table_schema = 'public'
|
||||
ORDER BY table_name
|
||||
`);
|
||||
|
||||
|
||||
// Filter tables by access rights (folders)
|
||||
const accessibleTables = result.rows.filter(table => canAccessTable(role, table.name));
|
||||
|
||||
// Get row counts for each table
|
||||
const tablesWithCounts = await Promise.all(
|
||||
result.rows.map(async (table) => {
|
||||
accessibleTables.map(async (table) => {
|
||||
try {
|
||||
const countResult = await pool.query(`SELECT COUNT(*) as count FROM "${table.name}"`);
|
||||
return {
|
||||
@@ -169,6 +251,13 @@ app.get('/api/tables', requireAuth, async (req, res) => {
|
||||
// Get table data with pagination, search, filters and sort
|
||||
app.get('/api/tables/:tableName/data', requireAuth, async (req, res) => {
|
||||
const { tableName } = req.params;
|
||||
const role = req.session.role || 'viewer';
|
||||
|
||||
// Check access for this table
|
||||
if (!canAccessTable(role, tableName)) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 10;
|
||||
const search = req.query.search || '';
|
||||
@@ -279,7 +368,14 @@ app.get('/api/tables/:tableName/structure', requireAuth, async (req, res) => {
|
||||
// Create table
|
||||
app.post('/api/tables', requireAuth, async (req, res) => {
|
||||
const { name, columns } = req.body;
|
||||
|
||||
const role = req.session.role || 'viewer';
|
||||
const folder = getTableFolder(name);
|
||||
|
||||
const perms = getRolePermissions(role);
|
||||
if (!perms.canCreate || (perms.folders && !perms.folders.includes(folder))) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
try {
|
||||
let columnsSQL = columns.map(col => {
|
||||
let def = `"${col.name}" ${col.type}`;
|
||||
@@ -300,6 +396,13 @@ app.post('/api/tables', requireAuth, async (req, res) => {
|
||||
// Delete table
|
||||
app.delete('/api/tables/:tableName', requireAuth, async (req, res) => {
|
||||
const { tableName } = req.params;
|
||||
const role = req.session.role || 'viewer';
|
||||
const folder = getTableFolder(tableName);
|
||||
const perms = getRolePermissions(role);
|
||||
|
||||
if (!perms.canDelete || (perms.folders && !perms.folders.includes(folder))) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
try {
|
||||
await pool.query(`DROP TABLE IF EXISTS "${tableName}"`);
|
||||
@@ -313,7 +416,14 @@ app.delete('/api/tables/:tableName', requireAuth, async (req, res) => {
|
||||
app.post('/api/tables/:tableName/records', requireAuth, async (req, res) => {
|
||||
const { tableName } = req.params;
|
||||
const data = req.body;
|
||||
|
||||
const role = req.session.role || 'viewer';
|
||||
const folder = getTableFolder(tableName);
|
||||
const perms = getRolePermissions(role);
|
||||
|
||||
if (!perms.canEdit || (perms.folders && !perms.folders.includes(folder))) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Get table structure to check types
|
||||
const structureResult = await pool.query(`
|
||||
@@ -362,8 +472,14 @@ app.post('/api/tables/:tableName/records', requireAuth, async (req, res) => {
|
||||
// Update record
|
||||
app.put('/api/tables/:tableName/records/:pk', requireAuth, async (req, res) => {
|
||||
const { tableName, pk } = req.params;
|
||||
const role = req.session.role || 'viewer';
|
||||
const folder = getTableFolder(tableName);
|
||||
const perms = getRolePermissions(role);
|
||||
if (!perms.canEdit || (perms.folders && !perms.folders.includes(folder))) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const data = req.body;
|
||||
|
||||
const columns = Object.keys(data);
|
||||
const values = Object.values(data);
|
||||
const setClause = columns.map((col, i) => `"${col}" = $${i + 1}`).join(', ');
|
||||
@@ -381,7 +497,13 @@ app.put('/api/tables/:tableName/records/:pk', requireAuth, async (req, res) => {
|
||||
// Delete record
|
||||
app.delete('/api/tables/:tableName/records/:pk', requireAuth, async (req, res) => {
|
||||
const { tableName, pk } = req.params;
|
||||
|
||||
const role = req.session.role || 'viewer';
|
||||
const folder = getTableFolder(tableName);
|
||||
const perms = getRolePermissions(role);
|
||||
if (!perms.canDelete || (perms.folders && !perms.folders.includes(folder))) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
try {
|
||||
const primaryKey = await getPrimaryKeyColumn(tableName) || 'id';
|
||||
await pool.query(`DELETE FROM "${tableName}" WHERE "${primaryKey}" = $1`, [pk]);
|
||||
|
||||
Reference in New Issue
Block a user