uploaded
This commit is contained in:
89
admin/server/admin.js
Normal file
89
admin/server/admin.js
Normal file
@@ -0,0 +1,89 @@
|
||||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const { configureSecurityHeaders } = require('./middleware/securityHeaders');
|
||||
const { adminLimiter } = require('./middleware/rateLimiter');
|
||||
const adminRoutes = require('./routes/admin');
|
||||
|
||||
const adminApp = express();
|
||||
const ADMIN_PORT = Number(process.env.ADMIN_PORT || 3005);
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
|
||||
/**
|
||||
* Trust proxy for correct IP detection
|
||||
* Important: Only enable if behind a reverse proxy
|
||||
*/
|
||||
adminApp.set('trust proxy', process.env.TRUST_PROXY === 'true' ? true : 1);
|
||||
|
||||
configureSecurityHeaders(adminApp, isDevelopment);
|
||||
|
||||
adminApp.use((req, res, next) => {
|
||||
const allowedOrigins = [
|
||||
'http://localhost:3005',
|
||||
'http://localhost:3000',
|
||||
process.env.ADMIN_ORIGIN || '',
|
||||
].filter(Boolean);
|
||||
|
||||
const origin = req.get('origin');
|
||||
if (allowedOrigins.includes(origin)) {
|
||||
res.setHeader('Access-Control-Allow-Origin', origin);
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
// Body parser
|
||||
adminApp.use(express.json({ limit: '10mb' }));
|
||||
adminApp.use(express.urlencoded({ limit: '10mb', extended: true }));
|
||||
|
||||
// Global rate limiting
|
||||
adminApp.use(adminLimiter);
|
||||
|
||||
// Admin routes with /api/admin prefix
|
||||
adminApp.use('/api/admin', adminRoutes);
|
||||
|
||||
// Health check endpoint
|
||||
adminApp.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// 404 handler
|
||||
adminApp.use((req, res) => {
|
||||
res.status(404).json({
|
||||
error: 'Not found',
|
||||
path: req.path,
|
||||
});
|
||||
});
|
||||
|
||||
// Error handler
|
||||
adminApp.use((err, req, res, next) => {
|
||||
console.error('Unhandled error:', err);
|
||||
|
||||
if (isDevelopment) {
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error',
|
||||
});
|
||||
});
|
||||
|
||||
// Start admin server
|
||||
function startAdminServer() {
|
||||
adminApp.listen(ADMIN_PORT, () => {
|
||||
console.log(`EaglerTiers Admin API running on http://localhost:${ADMIN_PORT}`);
|
||||
console.log(`Admin endpoints require API key via Authorization header or ?key parameter`);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { adminApp, startAdminServer };
|
||||
18
admin/server/create-admin.js
Normal file
18
admin/server/create-admin.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// create-admin.js
|
||||
require('dotenv').config();
|
||||
const bcrypt = require('bcrypt');
|
||||
const { pool } = require('./db');
|
||||
|
||||
async function createSuperAdmin() {
|
||||
const username = 'admin'; // change as needed
|
||||
const password = 'your-strong-password';
|
||||
const hash = await bcrypt.hash(password, 10);
|
||||
await pool.query(
|
||||
'INSERT INTO admin_users (username, password_hash, role) VALUES (?, ?, ?)',
|
||||
[username, hash, 'superadmin']
|
||||
);
|
||||
console.log('Superadmin created');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
createSuperAdmin().catch(console.error);
|
||||
96
admin/server/db.js
Normal file
96
admin/server/db.js
Normal file
@@ -0,0 +1,96 @@
|
||||
// db.js
|
||||
const mysql = require('mysql2/promise');
|
||||
const path = require('path');
|
||||
require('dotenv').config({ path: path.join(__dirname, '.env') });
|
||||
|
||||
const {
|
||||
DB_HOST = '127.0.0.1',
|
||||
DB_PORT = '3306',
|
||||
DB_USER = 'eaglertiers',
|
||||
DB_PASSWORD = 'eagler_local_dev_2026',
|
||||
DB_NAME = 'eaglertiers',
|
||||
} = process.env;
|
||||
|
||||
// Create a connection pool (used by the app)
|
||||
const pool = mysql.createPool({
|
||||
host: DB_HOST,
|
||||
port: Number(DB_PORT),
|
||||
user: DB_USER,
|
||||
password: DB_PASSWORD,
|
||||
database: DB_NAME,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
enableKeepAlive: true,
|
||||
keepAliveInitialDelay: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures the database and required tables exist.
|
||||
* Call this once during server startup.
|
||||
*/
|
||||
async function ensureDatabase() {
|
||||
// First, connect without database to create it if necessary
|
||||
const rootPool = mysql.createPool({
|
||||
host: DB_HOST,
|
||||
port: Number(DB_PORT),
|
||||
user: DB_USER,
|
||||
password: DB_PASSWORD,
|
||||
connectionLimit: 1,
|
||||
});
|
||||
|
||||
try {
|
||||
// Create database if it doesn't exist
|
||||
await rootPool.query(`CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\``);
|
||||
console.log(`Database "${DB_NAME}" ensured.`);
|
||||
|
||||
// Now use the main pool (with database selected) to create tables
|
||||
const connection = await pool.getConnection();
|
||||
|
||||
// Create players table
|
||||
await connection.query(`
|
||||
CREATE TABLE IF NOT EXISTS players (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(16) UNIQUE NOT NULL,
|
||||
region VARCHAR(2) NOT NULL DEFAULT 'NA',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// Create player_ranks table
|
||||
await connection.query(`
|
||||
CREATE TABLE IF NOT EXISTS player_ranks (
|
||||
player_id INT NOT NULL,
|
||||
gamemode VARCHAR(20) NOT NULL,
|
||||
tier VARCHAR(3) NOT NULL,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (player_id, gamemode),
|
||||
FOREIGN KEY (player_id) REFERENCES players(id) ON DELETE CASCADE
|
||||
)
|
||||
`);
|
||||
|
||||
// Create admin_users table (if you haven't already)
|
||||
await connection.query(`
|
||||
CREATE TABLE IF NOT EXISTS admin_users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
role ENUM('admin','superadmin') DEFAULT 'admin',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
connection.release();
|
||||
console.log('All tables ensured.');
|
||||
} catch (err) {
|
||||
console.error('Failed to ensure database:', err);
|
||||
throw err;
|
||||
} finally {
|
||||
await rootPool.end();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
pool,
|
||||
ensureDatabase,
|
||||
};
|
||||
Reference in New Issue
Block a user