Next.js+React web interface for controlling HAProxy clusters (groups of servers), in conjunction with with https://gitgud.io/fatchan/haproxy-protection.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
5.2 KiB
140 lines
5.2 KiB
process
|
|
.on('uncaughtException', console.error)
|
|
.on('unhandledRejection', console.error);
|
|
|
|
//Dependencies
|
|
const express = require('express');
|
|
const bodyParser = require('body-parser');
|
|
const cookieParser = require('cookie-parser')
|
|
const dotenv = require('dotenv');
|
|
dotenv.config({ path: '.env' });
|
|
const HAProxy = require('@fatchan/haproxy-sdk');
|
|
const session = require('express-session');
|
|
const MongoStore = require('connect-mongo');
|
|
const csrf = require('csurf');
|
|
const db = require('./db.js');
|
|
|
|
//Controllers
|
|
const mapsController = require('./controllers/maps');
|
|
const accountsController = require('./controllers/accounts');
|
|
const domainsController = require('./controllers/domains');
|
|
|
|
//Express setup
|
|
const app = express();
|
|
app.set('query parser', 'simple');
|
|
app.use(bodyParser.json({ extended: false })); // for parsing application/json
|
|
app.use(bodyParser.urlencoded({ extended: false })); // for parsing application/x-www-form-urlencoded
|
|
app.use(cookieParser(process.env.COOKIE_SECRET));
|
|
app.disable('x-powered-by');
|
|
app.set('view engine', 'pug');
|
|
app.set('views', './views/pages');
|
|
app.enable('view cache');
|
|
app.set('trust proxy', 1);
|
|
|
|
//template locals
|
|
app.locals.mapValueNames = { '0': 'None', '1': 'Proof-of-work', '2': 'hCaptcha' };
|
|
app.locals.fMap = require('./util.js').fMap;
|
|
|
|
async function run() {
|
|
//Session & auth
|
|
await db.connect();
|
|
const sessionStore = session({
|
|
secret: process.env.COOKIE_SECRET,
|
|
store: MongoStore.create({ mongoUrl: process.env.DB_URL }),
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
rolling: true,
|
|
cookie: {
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === 'production',
|
|
sameSite: 'strict',
|
|
maxAge: 1000 * 60 * 60 * 24 * 7, //week
|
|
}
|
|
});
|
|
const useSession = (req, res, next) => {
|
|
sessionStore(req, res, next);
|
|
}
|
|
const fetchSession = async (req, res, next) => {
|
|
if (req.session.user) {
|
|
const account = await db.db.collection('accounts').findOne({_id:req.session.user});
|
|
if (account) {
|
|
res.locals.user = {
|
|
username: account._id,
|
|
domains: account.domains,
|
|
clusters: account.clusters,
|
|
activeCluster: account.activeCluster,
|
|
};
|
|
return next();
|
|
}
|
|
req.session.destroy();
|
|
}
|
|
next();
|
|
};
|
|
const checkSession = (req, res, next) => {
|
|
if (!res.locals.user) {
|
|
return res.redirect('/login');
|
|
}
|
|
next();
|
|
};
|
|
const csrfMiddleware = csrf();
|
|
|
|
//HAProxy-sdk middleware
|
|
const useHaproxy = (req, res, next) => {
|
|
if (res.locals.user.clusters.length === 0) {
|
|
return next();
|
|
}
|
|
try {
|
|
//uses cluster from account
|
|
res.locals.haproxy = new HAProxy(res.locals.user.clusters[res.locals.user.activeCluster]);
|
|
next();
|
|
} catch (e) {
|
|
res.status(500).send(e);
|
|
}
|
|
};
|
|
|
|
const hasCluster = (req, res, next) => {
|
|
if (res.locals.user.clusters.length > 0) {
|
|
return next();
|
|
}
|
|
res.redirect('/clusters');
|
|
}
|
|
|
|
//static
|
|
app.use('/static', express.static('static'));
|
|
app.use('/static/css', express.static('node_modules/bootstrap/dist/css'));
|
|
|
|
//unauthed pages
|
|
app.get('/', useSession, fetchSession, accountsController.homePage);
|
|
app.get('/login', useSession, fetchSession, accountsController.loginPage);
|
|
app.get('/register', useSession, fetchSession, accountsController.registerPage);
|
|
|
|
//register/login/logout
|
|
app.post('/login', useSession, accountsController.login);
|
|
app.post('/logout', useSession, accountsController.logout);
|
|
app.post('/register', useSession, accountsController.register);
|
|
|
|
//authed pages that dont require a cluster
|
|
app.get('/account', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, accountsController.accountPage);
|
|
app.get('/domains', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, domainsController.domainsPage);
|
|
app.get('/clusters', useSession, fetchSession, checkSession, useHaproxy, csrfMiddleware, accountsController.clustersPage);
|
|
|
|
//authed pages that useHaproxy
|
|
const clusterRouter = express.Router({ caseSensitive: true });
|
|
clusterRouter.post('/global/toggle', accountsController.globalToggle);
|
|
clusterRouter.post('/cluster', accountsController.setCluster);
|
|
clusterRouter.post('/cluster/add', accountsController.addCluster);
|
|
clusterRouter.post('/cluster/delete', accountsController.deleteClusters);
|
|
clusterRouter.post('/domain/add', domainsController.addDomain);
|
|
clusterRouter.post('/domain/delete', domainsController.deleteDomain);
|
|
clusterRouter.get(`/maps/:name(${process.env.BLOCKED_MAP_NAME}|${process.env.MAINTENANCE_MAP_NAME}|${process.env.WHITELIST_MAP_NAME}|${process.env.BLOCKED_MAP_NAME}|${process.env.DDOS_MAP_NAME}|${process.env.HOSTS_MAP_NAME})`, mapsController.getMapHtml);
|
|
clusterRouter.post(`/maps/:name(${process.env.BLOCKED_MAP_NAME}|${process.env.MAINTENANCE_MAP_NAME}|${process.env.WHITELIST_MAP_NAME}|${process.env.DDOS_MAP_NAME}|${process.env.HOSTS_MAP_NAME})/add`, mapsController.patchMapForm); //add to MAP
|
|
clusterRouter.post(`/maps/:name(${process.env.BLOCKED_MAP_NAME}|${process.env.MAINTENANCE_MAP_NAME}|${process.env.WHITELIST_MAP_NAME}|${process.env.DDOS_MAP_NAME}|${process.env.HOSTS_MAP_NAME})/delete`, mapsController.deleteMapForm); //delete from MAP
|
|
app.use('/', useSession, fetchSession, checkSession, useHaproxy, hasCluster, csrfMiddleware, clusterRouter);
|
|
|
|
app.listen(8080, () => {
|
|
console.log('Running at http://localhost:8080 in %s mode', process.env.NODE_ENV);
|
|
});
|
|
|
|
}
|
|
|
|
run();
|
|
|