Compare commits

..

No commits in common. "af896a668815be9888be88dbc0dc36ee9f7338a3" and "f52897fd4d9b52dbc6ca758a0f67c4041c33d2c5" have entirely different histories.

11 changed files with 17 additions and 333 deletions

139
package-lock.json generated
View File

@ -21,11 +21,8 @@
"eta": "^2.0.1", "eta": "^2.0.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-fileupload": "^1.4.0", "express-fileupload": "^1.4.0",
"express-session": "^1.17.3",
"jquery": "^3.6.4", "jquery": "^3.6.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"signale": "^1.4.0", "signale": "^1.4.0",
"tsparticles-confetti": "^2.9.3" "tsparticles-confetti": "^2.9.3"
}, },
@ -33,10 +30,7 @@
"@loancrate/prisma-schema-parser": "^2.0.0", "@loancrate/prisma-schema-parser": "^2.0.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/express-fileupload": "^1.4.1", "@types/express-fileupload": "^1.4.1",
"@types/express-session": "^1.17.7",
"@types/lodash": "^4.14.194", "@types/lodash": "^4.14.194",
"@types/passport": "^1.0.12",
"@types/passport-local": "^1.0.35",
"@types/signale": "^1.4.4", "@types/signale": "^1.4.4",
"eslint": "^8.39.0", "eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",
@ -838,15 +832,6 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/express-session": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.7.tgz",
"integrity": "sha512-L25080PBYoRLu472HY/HNCxaXY8AaGgqGC8/p/8+BYMhG0RDOLQ1wpXOpAzr4Gi5TGozTKyJv5BVODM5UNyVMw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/lodash": { "node_modules/@types/lodash": {
"version": "4.14.194", "version": "4.14.194",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz",
@ -883,36 +868,6 @@
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
"dev": true "dev": true
}, },
"node_modules/@types/passport": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz",
"integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/passport-local": {
"version": "1.0.35",
"resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.35.tgz",
"integrity": "sha512-K4eLTJ8R0yYW8TvCqkjB0pTKoqfUSdl5PfZdidTjV2ETV3604fQxtY6BHKjQWAx50WUS0lqzBvKv3LoI1ZBPeA==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/passport": "*",
"@types/passport-strategy": "*"
}
},
"node_modules/@types/passport-strategy": {
"version": "0.2.35",
"resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
"integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
"dev": true,
"dependencies": {
"@types/express": "*",
"@types/passport": "*"
}
},
"node_modules/@types/qs": { "node_modules/@types/qs": {
"version": "6.9.7", "version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@ -2382,32 +2337,6 @@
"node": ">=12.0.0" "node": ">=12.0.0"
} }
}, },
"node_modules/express-session": {
"version": "1.17.3",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
"dependencies": {
"cookie": "0.4.2",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/express-session/node_modules/cookie": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express/node_modules/body-parser": { "node_modules/express/node_modules/body-parser": {
"version": "1.20.1", "version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
@ -4282,14 +4211,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -4501,42 +4422,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/passport": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
"dependencies": {
"passport-strategy": "1.x.x",
"pause": "0.0.1",
"utils-merge": "^1.0.1"
},
"engines": {
"node": ">= 0.4.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-local": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
"dependencies": {
"passport-strategy": "1.x.x"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/path-exists": { "node_modules/path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -4584,11 +4469,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
},
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@ -5236,14 +5116,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -6497,17 +6369,6 @@
"node": ">=12.20" "node": ">=12.20"
} }
}, },
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/unbox-primitive": { "node_modules/unbox-primitive": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",

View File

@ -29,11 +29,8 @@
"eta": "^2.0.1", "eta": "^2.0.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-fileupload": "^1.4.0", "express-fileupload": "^1.4.0",
"express-session": "^1.17.3",
"jquery": "^3.6.4", "jquery": "^3.6.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"signale": "^1.4.0", "signale": "^1.4.0",
"tsparticles-confetti": "^2.9.3" "tsparticles-confetti": "^2.9.3"
}, },
@ -41,10 +38,7 @@
"@loancrate/prisma-schema-parser": "^2.0.0", "@loancrate/prisma-schema-parser": "^2.0.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/express-fileupload": "^1.4.1", "@types/express-fileupload": "^1.4.1",
"@types/express-session": "^1.17.7",
"@types/lodash": "^4.14.194", "@types/lodash": "^4.14.194",
"@types/passport": "^1.0.12",
"@types/passport-local": "^1.0.35",
"@types/signale": "^1.4.4", "@types/signale": "^1.4.4",
"eslint": "^8.39.0", "eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.8.0",

View File

@ -6,18 +6,6 @@
<div class="col-9"></div> <div class="col-9"></div>
<div class="col-3 sidePanel ps-4 text-black"> <div class="col-3 sidePanel ps-4 text-black">
<h1>Log into AssetFlow</h1> <h1>Log into AssetFlow</h1>
<form action="/auth/login" method="post">
<div class="mb-3">
<label for="userName" class="form-label">Username</label>
<input name="username" type="text" class="form-control" id="userName" aria-describedby="userNameHelp" />
<!-- <div id="userNameHelp" class="form-text">We'll never share your email with anyone else.</div> -->
</div>
<div class="mb-3">
<label for="userPassword" class="form-label">Password</label>
<input name="password" type="password" class="form-control" id="userPassword" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div> </div>
</div> </div>
<%~ E.includeFile("../partials/foot.eta.html") %> <%~ E.includeFile("../partials/foot.eta.html") %>

View File

@ -5,8 +5,6 @@ import fileUpload from 'express-fileupload';
import { PrismaClient } from '@prisma/client'; import { PrismaClient } from '@prisma/client';
import * as eta from 'eta'; import * as eta from 'eta';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import session from 'express-session';
import passport from 'passport';
// Sentry // Sentry
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
@ -30,7 +28,6 @@ export const log = {
core: coreLogger, core: coreLogger,
db: coreLogger.scope('DB'), db: coreLogger.scope('DB'),
web: coreLogger.scope('WEB'), web: coreLogger.scope('WEB'),
auth: coreLogger.scope('AUTH'),
helper: coreLogger.scope('HELPER') helper: coreLogger.scope('HELPER')
}; };
@ -40,22 +37,9 @@ export const config = new ConfigHandler(__path + '/config.json', {
http_listen_address: '127.0.0.1', http_listen_address: '127.0.0.1',
http_port: 3000, http_port: 3000,
sentry_dsn: 'https://ID@sentry.example.com/PROJECTID', sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
debug: false, debug: false
auth: {
local: {
active: true,
users: {
user: 'password',
user1: 'password'
}
},
oidc: {
active: false
}
}
}); });
// TODO: Add errorhandling with some sort of message.
export const prisma = new PrismaClient({ export const prisma = new PrismaClient({
datasources: { datasources: {
db: { db: {
@ -83,16 +67,16 @@ Sentry.init({
environment: config.global.debug ? 'development' : 'production' environment: config.global.debug ? 'development' : 'production'
}); });
// TODO: Version check need to be rewritten. app.locals.versionRevLong = require('child_process')
app.locals.versionRevLong = require('child_process').execSync('git rev-parse HEAD').toString().trim(); .execSync('git rev-parse HEAD')
app.locals.versionRev = require('child_process').execSync('git rev-parse --short HEAD').toString().trim(); .toString().trim()
app.locals.versionRevLatest = require('child_process').execSync('git ls-remote --refs -q').toString().trim().split('\t')[0]; app.locals.versionRev = require('child_process')
.execSync('git rev-parse --short HEAD')
if (app.locals.versionRevLong === app.locals.versionRevLatest) { .toString().trim()
log.core.info(`Running Latest Version (${app.locals.versionRevLong})`); app.locals.versionRevLatest = require('child_process')
} else { .execSync('git ls-remote --refs -q')
log.core.info(`Running Version: ${app.locals.versionRevLong} (Latest: ${app.locals.versionRevLatest})`); .toString().trim().split("\t")[0]
} log.core.info(`Running revision ${app.locals.versionRevLong} (${app.locals.versionRevLatest} latest)`);
// RequestHandler creates a separate execution context using domains, so that every // RequestHandler creates a separate execution context using domains, so that every
// transaction/span/breadcrumb is attached to its own Hub instance // transaction/span/breadcrumb is attached to its own Hub instance
@ -109,23 +93,14 @@ app.use(bodyParser.urlencoded({ extended: false }));
// Using bodyParser to parse JSON bodies into JS objects // Using bodyParser to parse JSON bodies into JS objects
app.use(bodyParser.json()); app.use(bodyParser.json());
// Session store
// TODO: Move secret to config -> Autogenerate.
app.use(
session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
cookie: { secure: false }
})
);
app.use(passport.authenticate('session'));
app.use(fileUpload()); app.use(fileUpload());
app.use(express.static(__path + '/static')); app.use(express.static(__path + '/static'));
app.use(routes); app.use(routes);
// The error handler must be before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());
app.listen(config.global.http_port, config.global.http_listen_address, () => { app.listen(config.global.http_port, config.global.http_listen_address, () => {
log.web.info(`Listening at http://${config.global.http_listen_address}:${config.global.http_port}`); log.web.info(`Listening at http://${config.global.http_listen_address}:${config.global.http_port}`);
}); });

View File

@ -1,23 +0,0 @@
/*
function checkAuthentication(req: any, res: any, next: Function) {
if (req.isAuthenticated()) {
//req.isAuthenticated() will return true if user is logged in
next();
} else {
res.redirect('/auth/login');
}
}
const checkIsInRole = (...roles) => (req, res, next) => {
if (!req.user) {
return res.redirect('/login')
}
const hasRole = roles.find(role => req.user.role === role)
if (!hasRole) {
return res.redirect('/login')
}
return next()
}
*/

View File

@ -1,5 +1,4 @@
import express from 'express'; import express from 'express';
import passport from 'passport';
// Route imports // Route imports
import testRoute from './test.js'; import testRoute from './test.js';

View File

@ -1,96 +0,0 @@
import passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import express, { Request, Response } from 'express';
import { prisma, __path, log, config, app } from '../../index.js';
/* Configure password authentication strategy.
*
* The `LocalStrategy` authenticates users by verifying a username and password.
* The strategy parses the username and password from the request and calls the
* `verify` function.
*
* The `verify` function queries the database for the user record and verifies
* the password by hashing the password supplied by the user and comparing it to
* the hashed password stored in the database. If the comparison succeeds, the
* user is authenticated; otherwise, not.
*/
passport.use(
new LocalStrategy(function verify(username, password, cb) {
//log.auth.debug('LocalStrategy:', username, password);
for (const [user, pass] of Object.entries(config.global.auth.local.users)) {
//log.auth.debug('Loop(REQ):', username, password);
//log.auth.debug('Loop(CFG):', user, pass);
if (user === username && pass === password) {
log.auth.debug('LocalStrategy: success');
return cb(null, { username: username }); // This is the user object.
}
}
log.auth.debug('LocalStrategy: failed');
return cb(null, false, { message: 'Incorrect username or password.' });
})
/*
1. If the user not found in DB,
done (null, false)
2. If the user found in DB, but password does not match,
done (null, false)
3. If user found in DB and password match,
done (null, {authenticated_user})
*/
);
/* Configure session management.
*
* When a login session is established, information about the user will be
* stored in the session. This information is supplied by the `serializeUser`
* function, which is yielding the user ID and username.
*
* As the user interacts with the app, subsequent requests will be authenticated
* by verifying the session. The same user information that was serialized at
* session establishment will be restored when the session is authenticated by
* the `deserializeUser` function.
*
*/
passport.serializeUser(function (user: any, cb) {
process.nextTick(function () {
log.auth.debug('Called seriealizeUser');
log.auth.debug('user:', user);
return cb(null, {
username: user.username
});
});
});
passport.deserializeUser(function (user, cb) {
process.nextTick(function () {
log.auth.debug('Called deseriealizeUser');
return cb(null, user);
});
});
// Route imports
import testRoute from './test.js';
import loginRoute from './login.js';
//import logoutRoute from './login.js'
// Router base is '/auth'
const Router = express.Router({ strict: false });
Router.route('/login').get(loginRoute.get);
Router.route('/login').post(passport.authenticate('local', { successRedirect: '/', failureRedirect: '/auth/login?failed' }));
Router.route('/test').get(checkAuthentication, testRoute.get);
export default Router;
function checkAuthentication(req: Request, res: Response, next: Function) {
if (req.isAuthenticated()) {
//req.isAuthenticated() will return true if user is logged in
next();
} else {
res.redirect('/auth/login');
}
}

View File

@ -1,7 +0,0 @@
import express, { Request, Response } from 'express';
function get(req: Request, res: Response) {
res.status(200).send('Auth Test Successful!');
};
export default { get };

View File

@ -1,7 +1,5 @@
import passport from 'passport';
import express, { Request, Response } from 'express'; import express, { Request, Response } from 'express';
import { prisma, __path, log } from '../../index.js'; import { prisma, __path } from '../../../index.js';
function get(req: Request, res: Response) { function get(req: Request, res: Response) {
res.render(__path + '/src/frontend/auth/login.eta.html'); //, { items: items }); res.render(__path + '/src/frontend/auth/login.eta.html'); //, { items: items });

View File

@ -7,6 +7,7 @@ import jsonImportRoute from './import/jsonImport.js';
import categoryManager from './categoryManager.js'; import categoryManager from './categoryManager.js';
import storageManager from './storageManager.js'; import storageManager from './storageManager.js';
import startpageRoute from './startpage.js'; import startpageRoute from './startpage.js';
import demoLoginRoute from './demo.js'
// Router base is '/manage' // Router base is '/manage'
const Router = express.Router({ strict: false }); const Router = express.Router({ strict: false });
@ -16,6 +17,7 @@ Router.route('/categories').get(categoryManager.get);
Router.route('/storages').get(storageManager.get); Router.route('/storages').get(storageManager.get);
Router.route('/import/csv').get(csvImportRoute.get).post(csvImportRoute.post); Router.route('/import/csv').get(csvImportRoute.get).post(csvImportRoute.post);
Router.route('/import/json').get(jsonImportRoute.get).post(jsonImportRoute.post); Router.route('/import/json').get(jsonImportRoute.get).post(jsonImportRoute.post);
Router.route('/demo/login').get(demoLoginRoute.get);
Router.route('/').get(startpageRoute.get); Router.route('/').get(startpageRoute.get);
export default Router; export default Router;

View File

@ -1,23 +1,17 @@
import express, { Express } from 'express'; import express, { Express } from 'express';
import { __path, prisma } from '../index.js'; import { __path, prisma } from '../index.js';
import * as Sentry from '@sentry/node';
// Route imports // Route imports
import frontend_routes from './frontend/index.js'; import frontend_routes from './frontend/index.js';
import static_routes from './static/index.js'; import static_routes from './static/index.js';
import api_routes from './api/index.js'; import api_routes from './api/index.js';
import auth_routes from './auth/index.js';
const Router = express.Router({ strict: false }); const Router = express.Router({ strict: false });
Router.use('/static', static_routes); Router.use('/static', static_routes);
Router.use('/api', api_routes); Router.use('/api', api_routes);
Router.use('/auth', auth_routes);
Router.use('/', frontend_routes); Router.use('/', frontend_routes);
// The error handler must be before any other error middleware and after all controllers
Router.use(Sentry.Handlers.errorHandler());
// Default route. // Default route.
Router.all('*', function (req, res) { Router.all('*', function (req, res) {
// TODO: Respond based on content-type (with req.is('application/json')) // TODO: Respond based on content-type (with req.is('application/json'))
@ -29,4 +23,3 @@ Router.all('*', function (req, res) {
}); });
export default Router; export default Router;