Boostrap alerts for errors in api and use dynamicresponse

develop
Thomas Lynch 2 years ago
parent 692705538d
commit f777edb5b9
  1. 71
      api.js
  2. 7
      components/ErrorAlert.js
  3. 22
      controllers/account.js
  4. 26
      controllers/clusters.js
  5. 12
      controllers/domains.js
  6. 32
      controllers/maps.js
  7. 21
      pages/account.js
  8. 13
      pages/clusters.js
  9. 21
      pages/domains.js
  10. 9
      pages/login.js
  11. 21
      pages/map/[name].js
  12. 8
      pages/register.js
  13. 9
      router.js
  14. 14
      util.js

@ -1,35 +1,62 @@
import NProgress from 'nprogress';
export default async function ApiCall(route, method, body, stateCallback, finishProgress, router) {
try {
const options = {
// redirect: "manual",
method,
};
if (body != null) {
options.body = body;
options.headers = { 'Content-Type': 'application/json' };
function buildOptions(route, method, body) {
// Convert method uppercase
method = method.toUpperCase();
const options = {
redirect: "manual",
method,
headers: {
'Content-Type': 'application/json',
}
NProgress.start();
let response = await fetch(route, options);
// if (response && response.ok) {
response = await response.json();
console.log(response);
stateCallback && stateCallback(response);
// } else {
// //TODO: handle bad form submissions by including "error" prop into callback
// router.push('/login');
// }
};
if (body != null) {
options.body = body;
}
return options;
}
export default async function ApiCall(route, method='get', body, stateCallback, errorCallback, finishProgress, router) {
// Start progress bar
NProgress.start();
// Build request options for fetch
const requestOptions = buildOptions(route, method, body);
// Make request, catch errors, and finally{} to always end progress bar
let response;
try {
response = await fetch(route, requestOptions);
} catch(e) {
console.error(e);
//TODO: handle bad form submissions by including "error" prop into callback
// router.push('/login');
} finally {
if (finishProgress != null) {
NProgress.set(finishProgress);
} else {
NProgress.done(true);
}
return null;
}
if (!response) {
errorCallback && errorCallback('An error occurred');
return;
}
// Process request response
const contentType = response.headers.get('Content-type');
if (contentType.startsWith('application/json;')) {
response = await response.json();
if (response.redirect) {
return router.push(response.redirect, null, { scroll: false });
} else if (response.error) {
errorCallback && errorCallback(response.error);
return;
}
stateCallback && stateCallback(response);
} else {
errorCallback && errorCallback('An error occurred');
}
}

@ -0,0 +1,7 @@
export default function ErrorAlert({ error }) {
return (
<div className="alert alert-danger" role="alert">
{error}
</div>
)
}

@ -1,6 +1,6 @@
const bcrypt = require('bcrypt');
const db = require('../db.js');
const { validClustersString, makeArrayIfSingle, extractMap } = require('../util.js');
const { validClustersString, makeArrayIfSingle, extractMap, dynamicResponse } = require('../util.js');
/**
* account page data shared between html/json routes
@ -63,7 +63,7 @@ exports.accountJson = async (req, res, next) => {
*/
exports.globalToggle = async (req, res, next) => {
if (res.locals.user.username !== "admin") {
res.status(403).send('only admin can toggle global');
return dynamicResponse(req, res, 403, { error: 'Only admin can toggle global' });
}
let globalIndex;
try {
@ -87,7 +87,7 @@ exports.globalToggle = async (req, res, next) => {
} catch (e) {
return next(e);
}
return res.redirect('/account');
return dynamicResponse(req, res, 302, { redirect: '/account' });
};
/**
@ -99,14 +99,14 @@ exports.login = async (req, res) => {
const password = req.body.password;
const account = await db.db.collection('accounts').findOne({_id:username});
if (!account) {
return res.status(403).send('Incorrect username or password');
return dynamicResponse(req, res, 403, { error: 'Incorrect username or password' });
}
const passwordMatch = await bcrypt.compare(password, account.passwordHash);
if (passwordMatch === true) {
req.session.user = account._id;
return res.redirect('/account');
return dynamicResponse(req, res, 302, { redirect: '/account' });
}
return res.status(403).send('Incorrect username or password');
return dynamicResponse(req, res, 403, { error: 'Incorrect username or password' });
};
/**
@ -122,16 +122,16 @@ exports.register = async (req, res) => {
|| !password || typeof password !== "string" || password.length === 0
|| !rPassword || typeof rPassword !== "string" || rPassword.length === 0) {
//todo: length limits, make jschan input validator LGPL lib and use here
return res.status(400).send('Invalid inputs');
return dynamicResponse(req, res, 400, { error: 'Invalid inputs' });
}
if (password !== rPassword) {
return res.status(400).send('Passwords did not match');
return dynamicResponse(req, res, 400, { error: 'Passwords did not match' });
}
const existingAccount = await db.db.collection('accounts').findOne({ _id: username });
if (existingAccount) {
return res.status(409).send('Account already exists with that username');
return dynamicResponse(req, res, 409, { error: 'Account already exists with this username' });
}
const passwordHash = await bcrypt.hash(req.body.password, 12);
@ -147,7 +147,7 @@ exports.register = async (req, res) => {
balance: 0,
});
return res.redirect('/login');
return dynamicResponse(req, res, 302, { redirect: '/login' });
};
/**
@ -156,5 +156,5 @@ exports.register = async (req, res) => {
*/
exports.logout = (req, res) => {
req.session.destroy();
return res.redirect('/login');
return dynamicResponse(req, res, 302, { redirect: '/login' });
};

@ -1,5 +1,5 @@
const db = require('../db.js');
const { validClustersString, makeArrayIfSingle, extractMap } = require('../util.js');
const { validClustersString, makeArrayIfSingle, extractMap, dynamicResponse } = require('../util.js');
exports.clustersPage = async (app, req, res, next) => {
return app.render(req, res, '/clusters', {
@ -44,7 +44,7 @@ exports.globalToggle = async (req, res, next) => {
} catch (e) {
return next(e);
}
return res.redirect('/account');
return dynamicResponse(req, res, 302, { redirect: '/account' });
};
/**
@ -53,15 +53,15 @@ exports.globalToggle = async (req, res, next) => {
*/
exports.setCluster = async (req, res, next) => {
if (res.locals.user.username !== "admin") {
return res.status(403).send('only admin can change cluster');
return dynamicResponse(req, res, 403, { error: 'Only admin can change cluster' });
}
if (req.body == null || req.body.cluster == null) {
return res.status(400).send('invalid cluster');
return dynamicResponse(req, res, 404, { error: 'Invalid cluster' });
}
req.body.cluster = parseInt(req.body.cluster, 10) || 0;
if (!Number.isSafeInteger(req.body.cluster)
|| req.body.cluster > res.locals.user.clusters.length-1) {
return res.status(404).send('invalid cluster');
return dynamicResponse(req, res, 404, { error: 'Invalid cluster' });
}
try {
await db.db.collection('accounts')
@ -69,7 +69,7 @@ exports.setCluster = async (req, res, next) => {
} catch (e) {
return next(e);
}
return res.redirect('/account');
return dynamicResponse(req, res, 302, { redirect: '/account' });
};
/**
@ -78,12 +78,12 @@ exports.setCluster = async (req, res, next) => {
*/
exports.addCluster = async (req, res, next) => {
if (res.locals.user.username !== "admin") {
return res.status(403).send('only admin can add cluster');
return dynamicResponse(req, res, 404, { error: 'Only admin can add cluster' });
}
if (!req.body || !req.body.cluster
|| typeof req.body.cluster !== 'string'
|| !validClustersString(req.body.cluster)) {
return res.status(400).send('invalid cluster');
return dynamicResponse(req, res, 400, { error: 'Invalid cluster' });
}
try {
await db.db.collection('accounts')
@ -91,7 +91,7 @@ exports.addCluster = async (req, res, next) => {
} catch (e) {
return next(e);
}
return res.redirect('/clusters');
return dynamicResponse(req, res, 302, { redirect: '/clusters' });
};
/**
@ -100,17 +100,17 @@ exports.addCluster = async (req, res, next) => {
*/
exports.deleteClusters = async (req, res, next) => {
if (res.locals.user.username !== "admin") {
return res.status(403).send('only admin can delete cluster');
return dynamicResponse(req, res, 404, { error: 'Only admin can delete cluster' });
}
const existingClusters = new Set(res.locals.user.clusters);
req.body.cluster = makeArrayIfSingle(req.body.cluster);
if (!req.body || !req.body.cluster
|| !req.body.cluster.some(c => existingClusters.has(c))) {
return res.status(400).send('invalid cluster');
return dynamicResponse(req, res, 400, { error: 'Invalid cluster' });
}
const filteredClusters = res.locals.user.clusters.filter(c => !req.body.cluster.includes(c));
if (filteredClusters.length === 0) {
return res.status(400).send('cant delete last cluster');
return dynamicResponse(req, res, 400, { error: 'Cannot delete last cluster' });
}
let newActiveCluster = res.locals.user.activeCluster;
if (res.locals.user.activeCluster > filteredClusters.length-1) {
@ -122,5 +122,5 @@ exports.deleteClusters = async (req, res, next) => {
} catch (e) {
return next(e);
}
return res.redirect('/clusters');
return dynamicResponse(req, res, 302, { redirect: '/clusters' });
};

@ -1,6 +1,6 @@
const db = require('../db.js');
const url = require('url');
const { deleteFromMap } = require('../util.js');
const { deleteFromMap, dynamicResponse } = require('../util.js');
/**
* GET /domains
@ -30,13 +30,13 @@ exports.domainsJson = async (req, res) => {
exports.addDomain = async (req, res) => {
if (!req.body.domain || typeof req.body.domain !== 'string' || req.body.domain.length === 0) {
return res.status(400).send('Invalid input');
return dynamicResponse(req, res, 400, { error: 'Invalid input' });
}
try {
const { hostname } = url.parse(`https://${req.body.domain}`);
} catch (e) {
return res.status(400).send('Invalid input');
return dynamicResponse(req, res, 400, { error: 'Invalid input' });
}
//todo: somehow enforce payment? or probably not for now, lol
@ -44,7 +44,7 @@ exports.addDomain = async (req, res) => {
await db.db.collection('accounts')
.updateOne({_id: res.locals.user.username}, {$addToSet: {domains: req.body.domain }});
return res.redirect('/domains');
return dynamicResponse(req, res, 302, { redirect: '/domains' });
};
/**
@ -55,7 +55,7 @@ exports.deleteDomain = async (req, res) => {
if (!req.body.domain || typeof req.body.domain !== 'string' || req.body.domain.length === 0
|| !res.locals.user.domains.includes(req.body.domain)) {
return res.status(400).send('Invalid input');
return dynamicResponse(req, res, 400, { error: 'Invalid input' });
}
//will fail if domain is only in the hosts map for a different cluster, so we wont do it (for now)
@ -65,6 +65,6 @@ exports.deleteDomain = async (req, res) => {
await db.db.collection('accounts')
.updateOne({_id: res.locals.user.username}, {$pull: {domains: req.body.domain }});
return res.redirect('/domains');
return dynamicResponse(req, res, 302, { redirect: '/domains' });
};

@ -1,4 +1,4 @@
const { deleteFromMap, getMapId } = require('../util.js');
const { deleteFromMap, getMapId, dynamicResponse } = require('../util.js');
const { createCIDR, parse } = require('ip6addr');
const url = require('url');
/**
@ -12,7 +12,7 @@ exports.mapData = async (req, res, next) => {
try {
mapId = await getMapId(res.locals.haproxy, req.params.name);
if (!mapId) {
return res.status(400).send('invalid map');
return dynamicResponse(req, res, 400, { error: 'Invalid map' });
}
map = await res.locals.haproxy
.showMap(mapId.index);
@ -42,7 +42,7 @@ exports.mapData = async (req, res, next) => {
});
break;
default:
return res.status(400).send('invalid map');
return dynamicResponse(req, res, 400, { error: 'Invalid map' });
}
return {
@ -72,7 +72,7 @@ exports.mapJson = async (req, res, next) => {
*/
exports.deleteMapForm = async (req, res, next) => {
if(!req.body || !req.body.key || typeof req.body.key !== 'string' || req.body.key.length === 0) {
res.status(400).send('invalid value');
return dynamicResponse(req, res, 400, { error: 'Invalid value' });
}
if (req.params.name === process.env.HOSTS_MAP_NAME
@ -81,7 +81,7 @@ exports.deleteMapForm = async (req, res, next) => {
const { hostname } = url.parse(`https://${req.body.key}`);
const allowed = res.locals.user.domains.includes(hostname);
if (!allowed) {
return res.status(403).send('no permission for that domain');
return dynamicResponse(req, res, 403, { error: 'No permission for that domain' });
}
} else if (req.params.name === process.env.BLOCKED_MAP_NAME
|| req.params.name === process.env.WHITELIST_MAP_NAME) {
@ -114,7 +114,7 @@ exports.deleteMapForm = async (req, res, next) => {
}
await deleteFromMap(res.locals.haproxy, req.params.name, req.body.key);
return res.redirect(`/map/${req.params.name}`);
return dynamicResponse(req, res, 302, { redirect: `/map/${req.params.name}` });
} catch (e) {
return next(e);
}
@ -132,7 +132,7 @@ exports.patchMapForm = async (req, res, next) => {
//ddos must have valid 0, 1, 2
if (req.params.name === process.env.DDOS_MAP_NAME
&& (!req.body.value || !['0', '1', '2'].includes(req.body.value))) {
return res.status(400).send('invalid value');
return dynamicResponse(req, res, 400, { error: 'Invalid value' });
}
//ddos and hosts must have valid hostname
@ -142,7 +142,7 @@ exports.patchMapForm = async (req, res, next) => {
const { hostname, pathname } = url.parse(`https://${req.body.key}`);
const allowed = res.locals.user.domains.includes(hostname);
if (!allowed) {
return res.status(403).send('no permission for that domain');
return dynamicResponse(req, res, 403, { error: 'No permission for that domain' });
}
}
@ -158,7 +158,7 @@ exports.patchMapForm = async (req, res, next) => {
} catch (e) { parsedSubnet = null; /*invalid subnet or just an ip*/ }
const parsedIpOrSubnet = parsedIp || parsedSubnet;
if (!parsedIpOrSubnet) {
return res.status(400).send('invalid input');
return dynamicResponse(req, res, 400, { error: 'Invalid input' });
}
req.body.key = parsedIpOrSubnet.toString({zeroElide: false, zeroPad:false});
}
@ -169,11 +169,11 @@ exports.patchMapForm = async (req, res, next) => {
try {
parsedValue = url.parse(`https://${req.body.value}`)
if (!parsedValue.host || !parsedValue.port) {
return res.status(400).send('invalid input');
return dynamicResponse(req, res, 400, { error: 'Invalid input' });
}
parse(parsedValue.hostname); //better ip parsing, will error if invalid
} catch (e) {
return res.status(400).send('invalid input');
return dynamicResponse(req, res, 400, { error: 'Invalid input' });
}
req.body.value = parsedValue.host; //host includes port
}
@ -196,7 +196,7 @@ exports.patchMapForm = async (req, res, next) => {
value = res.locals.user.username;
break;
default:
return res.status(400).send('invalid map');
return dynamicResponse(req, res, 400, { error: 'Invalid map' });
}
try {
@ -215,7 +215,7 @@ exports.patchMapForm = async (req, res, next) => {
.backend(process.env.BACKEND_NAME);
let server;
if (backendMapEntry) {
return res.status(400).send(`this domain is active already and has a backend server mapping: "${backendMapEntry}"`);
return dynamicResponse(req, res, 400, { error: `this domain is active already and has a backend server mapping: "${backendMapEntry}"` });
} else {
//no existing backend map entry (i.e. didnt exist at startup to get constructed in the lua script)
let backendCounter = 0;
@ -240,7 +240,7 @@ exports.patchMapForm = async (req, res, next) => {
backendCounter++;
}
if (!server) {
return res.status(400).send('no server slots available');
return dynamicResponse(req, res, 400, { error: 'No server slots available' });
}
const backendsMapId = await getMapId(res.locals.haproxy, process.env.BACKENDS_MAP_NAME);
await res.locals.haproxy
@ -254,10 +254,10 @@ exports.patchMapForm = async (req, res, next) => {
const mapId = await getMapId(res.locals.haproxy, req.params.name);
await res.locals.haproxy
.addMap(mapId.index, req.body.key, value);
return res.redirect(`/map/${req.params.name}`);
return dynamicResponse(req, res, 302, { redirect: `/map/${req.params.name}` });
} catch (e) {
return next(e);
}
}
res.status(400).send('invalid value');
return dynamicResponse(req, res, 400, { error: 'Invalid value' });
};

@ -3,6 +3,7 @@ import Head from 'next/head';
import Link from 'next/link';
import MapLink from '../components/MapLink.js';
import LoadingPlaceholder from '../components/LoadingPlaceholder.js';
import ErrorAlert from '../components/ErrorAlert.js';
import ApiCall from '../api.js';
import { useRouter } from 'next/router';
@ -11,15 +12,21 @@ const Account = (props) => {
const router = useRouter();
const [accountData, setAccountData] = useState(props);
const [error, setError] = useState();
React.useEffect(() => {
if (!accountData.user) {
ApiCall('/account.json', 'GET', null, setAccountData, null, router);
ApiCall('/account.json', 'GET', null, setAccountData, setError, null, router);
}
}, [accountData.user, router]);
if (!accountData.user) {
return (<>Loading...</>);
return (
<>
Loading...
{error && <ErrorAlert error={error} />}
</>
);
}
const { user, maps, acls, globalAcl, csrf } = accountData;
@ -35,14 +42,14 @@ const Account = (props) => {
async function switchCluster(e) {
e.preventDefault();
await ApiCall('/forms/cluster', 'POST', JSON.stringify({ _csrf: csrf, cluster: nextCluster }), null, 0.5, router);
await ApiCall('/account.json', 'GET', null, setAccountData, null, router);
await ApiCall('/forms/cluster', 'POST', JSON.stringify({ _csrf: csrf, cluster: nextCluster }), null, setError, 0.5, router);
await ApiCall('/account.json', 'GET', null, setAccountData, setError, null, router);
}
async function toggleGlobal(e) {
e.preventDefault();
await ApiCall('/forms/global/toggle', 'POST', JSON.stringify({ _csrf: csrf }), null, 0.5, router);
await ApiCall('/account.json', 'GET', null, setAccountData, null, router);
await ApiCall('/forms/global/toggle', 'POST', JSON.stringify({ _csrf: csrf }), null, setError, 0.5, router);
await ApiCall('/account.json', 'GET', null, setAccountData, setError, null, router);
}
return (
@ -52,6 +59,8 @@ const Account = (props) => {
<title>Account</title>
</Head>
{error && <ErrorAlert error={error} />}
<h5 className="fw-bold">
Controls:
</h5>

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import Head from 'next/head';
import Link from 'next/link';
import BackButton from '../components/BackButton.js'
import ErrorAlert from '../components/ErrorAlert.js';
import ApiCall from '../api.js'
import { useRouter } from 'next/router';
@ -10,15 +11,21 @@ export default function Clusters(props) {
const router = useRouter();
const [accountData, setAccountData] = useState(props);
const [error, setError] = useState();
React.useEffect(() => {
if (!accountData.user) {
ApiCall('/clusters.json', 'GET', null, setAccountData, null, router);
ApiCall('/clusters.json', 'GET', null, setAccountData,setError, null, router);
}
}, [accountData.user, router]);
if (!accountData.user) {
return <>Loading...</>; //TODO: page with animated css placeholder boxes
return (
<>
Loading...
{error && <ErrorAlert error={error} />}
</>
);
}
const { user, csrf } = accountData;
@ -59,6 +66,8 @@ export default function Clusters(props) {
<title>Clusters</title>
</Head>
{error && <ErrorAlert error={error} />}
<h5 className="fw-bold">
Clusters ({user.clusters.length}):
</h5>

@ -3,6 +3,7 @@ import Head from 'next/head';
import Link from 'next/link';
import BackButton from '../components/BackButton.js';
import LoadingPlaceholder from '../components/LoadingPlaceholder.js';
import ErrorAlert from '../components/ErrorAlert.js';
import ApiCall from '../api.js';
import { useRouter } from 'next/router';
@ -11,29 +12,35 @@ export default function Domains(props) {
const router = useRouter();
const [accountData, setAccountData] = useState(props);
const [error, setError] = useState();
React.useEffect(() => {
if (!accountData.user) {
ApiCall('/domains.json', 'GET', null, setAccountData, null, router);
ApiCall('/domains.json', 'GET', null, setAccountData, setError, null, router);
}
}, [accountData.user, router]);
if (!accountData.user) {
return (<>Loading...</>);
return (
<>
Loading...
{error && <ErrorAlert error={error} />}
</>
);
}
const { user, csrf } = accountData;
async function addDomain(e) {
e.preventDefault();
await ApiCall('/forms/domain/add', 'POST', JSON.stringify({ _csrf: csrf, domain: e.target.domain.value }), null, 0.5, router);
await ApiCall('/domains.json', 'GET', null, setAccountData, null, router);
await ApiCall('/forms/domain/add', 'POST', JSON.stringify({ _csrf: csrf, domain: e.target.domain.value }), null, setError, 0.5, router);
await ApiCall('/domains.json', 'GET', null, setAccountData, setError, null, router);
}
async function deleteDomain(e) {
e.preventDefault();
await ApiCall('/forms/domain/delete', 'POST', JSON.stringify({ _csrf: csrf, domain: e.target.domain.value }), null, 0.5, router);
await ApiCall('/domains.json', 'GET', null, setAccountData, null, router);
await ApiCall('/forms/domain/delete', 'POST', JSON.stringify({ _csrf: csrf, domain: e.target.domain.value }), null, setError, 0.5, router);
await ApiCall('/domains.json', 'GET', null, setAccountData, setError, null, router);
}
const domainList = user.domains.map((d, i) => {
@ -60,6 +67,8 @@ export default function Domains(props) {
<title>Domains</title>
</Head>
{error && <ErrorAlert error={error} />}
<h5 className="fw-bold">
Available Domains:
</h5>

@ -1,18 +1,21 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
import ApiCall from '../api.js';
import ErrorAlert from '../components/ErrorAlert.js';
import { useState } from 'react';
const Login = () => {
const router = useRouter();
const [error, setError] = useState();
async function login(e) {
e.preventDefault();
await ApiCall('/forms/login', 'POST', JSON.stringify({
username: e.target.username.value,
password: e.target.password.value,
}), null, 0.5, router);
router.push('/account');
}), null, setError, null, router);
}
return (
@ -21,6 +24,8 @@ const Login = () => {
<title>Login</title>
</Head>
{error && <ErrorAlert error={error} />}
<h5 className="fw-bold">Login</h5>
<form onSubmit={login} action="/forms/login" method="POST">
<div className="mb-2">

@ -4,6 +4,7 @@ import Head from 'next/head';
import Link from 'next/link';
import MapRow from '../../components/MapRow.js';
import BackButton from '../../components/BackButton.js';
import ErrorAlert from '../../components/ErrorAlert.js';
import ApiCall from '../../api.js';
const MapPage = (props) => {
@ -13,30 +14,36 @@ const MapPage = (props) => {
const { name: mapName } = router.query;
const [mapData, setMapData] = useState(props);
const [error, setError] = useState();
React.useEffect(() => {
if (!mapData.user) {
ApiCall(`/map/${mapName}.json`, 'GET', null, setMapData, null, router);
ApiCall(`/map/${mapName}.json`, 'GET', null, setMapData, setError, null, router);
}
}, [mapData.user, mapName, router]);
if (!mapData.user) {
return <>Loading...</>; //todo: page with animated css placeholder boxes
return (
<>
Loading...
{error && <ErrorAlert error={error} />}
</>
);
}
const { user, mapValueNames, mapId, map, csrf, name, showValues } = mapData;
async function addToMap(e) {
e.preventDefault();
await ApiCall(`/forms/map/${mapId.name}/add`, 'POST', JSON.stringify({ _csrf: csrf, key: e.target.key.value, value: e.target.value?.value }), null, 0.5, router);
await ApiCall(`/map/${mapId.name}.json`, 'GET', null, setMapData, null, router);
await ApiCall(`/forms/map/${mapId.name}/add`, 'POST', JSON.stringify({ _csrf: csrf, key: e.target.key.value, value: e.target.value?.value }), null, setError, 0.5, router);
await ApiCall(`/map/${mapId.name}.json`, 'GET', null, setMapData, setError, null, router);
e.target.reset();
}
async function deleteFromMap(e) {
e.preventDefault();
await ApiCall(`/forms/map/${mapId.name}/delete`, 'POST', JSON.stringify({ _csrf: csrf, key: e.target.key.value }), null, 0.5, router);
await ApiCall(`/map/${mapId.name}.json`, 'GET', null, setMapData, null, router);
await ApiCall(`/forms/map/${mapId.name}/delete`, 'POST', JSON.stringify({ _csrf: csrf, key: e.target.key.value }), null, setError, 0.5, router);
await ApiCall(`/map/${mapId.name}.json`, 'GET', null, setMapData, setError, null, router);
}
const mapRows = map.map((row, i) => {
@ -112,6 +119,8 @@ const MapPage = (props) => {
</title>
</Head>
{error && <ErrorAlert error={error} />}
{/* Map friendly name (same as shown on acc page) */}
<h5 className="fw-bold">
{mapId.fname}:

@ -1,18 +1,22 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
import ApiCall from '../api.js';
import ErrorAlert from '../components/ErrorAlert.js';
import { useState } from 'react';
const Register = () => {
const router = useRouter();
const [error, setError] = useState();
async function register(e) {
e.preventDefault();
await ApiCall('/forms/regiser', 'POST', JSON.stringify({
username: e.target.username.value,
password: e.target.password.value,
rpasword: e.target.repeat_password.value,
}), null, 0.5, router);
}), null, setError, 0.5, router);
router.push('/login');
}
@ -22,6 +26,8 @@ const Register = () => {
<title>Register</title>
</Head>
{error && <ErrorAlert error={error} />}
<h5 className="fw-bold">Register</h5>
<form onSubmit={register} action="/forms/register" method="POST">
<div className="mb-2">

@ -4,7 +4,8 @@ const HAProxy = require('@fatchan/haproxy-sdk')
, session = require('express-session')
, MongoStore = require('connect-mongo')
, db = require('./db.js')
, csrf = require('csurf');
, csrf = require('csurf')
, { dynamicResponse } = require('./util.js');
const testRouter = (server, app) => {
@ -45,7 +46,7 @@ const testRouter = (server, app) => {
const checkSession = (req, res, next) => {
if (!res.locals.user) {
return res.redirect('/login');
return dynamicResponse(req, res, 302, { redirect: '/login' });
}
next();
};
@ -64,7 +65,7 @@ const testRouter = (server, app) => {
res.locals.mapValueNames = server.locals.mapValueNames;
next();
} catch (e) {
res.status(500).send(e);
return dynamicResponse(req, res, 500, { error: e });
}
};
@ -72,7 +73,7 @@ const testRouter = (server, app) => {
if (res.locals.user.clusters.length > 0) {
return next();
}
res.redirect('/clusters');
return dynamicResponse(req, res, 302, { redirect: '/clusters' });
};
//Controllers

@ -81,6 +81,18 @@ module.exports = {
return haproxy.delMap(mapId.index, value);
},
//todo: other map methods
dynamicResponse: async(req, res, code, data) => {
const isRedirect = code === 302;
if (req.headers && req.headers['content-type'] === 'application/json') {
return res
.status(isRedirect ? 200 : code)
.json(data);
}
if (isRedirect) {
return res.redirect(data.redirect);
}
//TODO: pass through app (bind to middleware) and app.render an "error" page for nojs users?
return res.status(code).send(data);
},
};

Loading…
Cancel
Save