diff --git a/src/web/httpstatus.js b/src/web/httpstatus.js
index eaba24d3..b2e2430d 100644
--- a/src/web/httpstatus.js
+++ b/src/web/httpstatus.js
@@ -1,3 +1,4 @@
export const BAD_REQUEST = 400;
export const FORBIDDEN = 403;
+export const NOT_FOUND = 404;
export const INTERNAL_SERVER_ERROR = 500;
diff --git a/src/web/routes/channel.js b/src/web/routes/channel.js
index 4a185f3e..8fcf1dea 100644
--- a/src/web/routes/channel.js
+++ b/src/web/routes/channel.js
@@ -7,7 +7,7 @@ import { HTTPError } from '../../errors';
export default function initialize(app, ioConfig) {
app.get('/r/:channel', (req, res) => {
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
- throw new HTTPError(`"${sanitizeText(req.params.channel)} is not a valid ` +
+ throw new HTTPError(`"${sanitizeText(req.params.channel)}" is not a valid ` +
'channel name.', { status: HTTPStatus.BAD_REQUEST });
}
diff --git a/src/web/webserver.js b/src/web/webserver.js
index 0b4bdc0a..c0c4b586 100644
--- a/src/web/webserver.js
+++ b/src/web/webserver.js
@@ -17,7 +17,7 @@ var session = require("../session");
var csrf = require("./csrf");
var XSS = require("../xss");
import * as HTTPStatus from './httpstatus';
-import { CSRFError } from '../errors';
+import { CSRFError, HTTPError } from '../errors';
const LOG_FORMAT = ':real-address - :remote-user [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"';
morgan.token('real-address', function (req) { return req._ip; });
@@ -211,11 +211,19 @@ module.exports = {
app.use(serveStatic(path.join(__dirname, "..", "..", "www"), {
maxAge: Config.get("http.max-age") || Config.get("http.cache-ttl")
}));
+ app.use((req, res, next) => {
+ return next(new HTTPError(`No route for ${req.path}`, {
+ status: HTTPStatus.NOT_FOUND
+ }));
+ });
app.use(function (err, req, res, next) {
if (err) {
if (err instanceof CSRFError) {
res.status(HTTPStatus.FORBIDDEN);
- return sendJade(res, 'csrferror', { path: req.path });
+ return sendJade(res, 'csrferror', {
+ path: req.path,
+ referer: req.header('referer')
+ });
}
let { message, status } = err;
@@ -226,11 +234,17 @@ module.exports = {
message = 'An unknown error occurred.';
}
+ // Log 5xx (server) errors
if (Math.floor(status / 100) === 5) {
Logger.errlog.log(err.stack);
}
- return res.status(status).send(message);
+ res.status(status);
+ return sendJade(res, 'httperror', {
+ path: req.path,
+ status: status,
+ message: message
+ });
} else {
next();
}
diff --git a/templates/csrferror.jade b/templates/csrferror.jade
index b67bd428..a3a8add3 100644
--- a/templates/csrferror.jade
+++ b/templates/csrferror.jade
@@ -24,7 +24,8 @@ html(lang="en")
li A malicious user has attempted to tamper with your session
li Your browser does not support cookies, or they are not enabled
| If the problem persists, please contact an administrator.
- a(href=path) Return to previous page
+ if referer
+ a(href=referer) Return to previous page
include footer
mixin footer()
diff --git a/templates/httperror.jade b/templates/httperror.jade
new file mode 100644
index 00000000..5ac23c71
--- /dev/null
+++ b/templates/httperror.jade
@@ -0,0 +1,36 @@
+mixin notfound()
+ h1 Not Found
+ p The page you were looking for doesn't seem to exist. Please check that you typed the URL correctly.
+mixin forbidden()
+ h1 Forbidden
+ p You don't have permission to access #{path}
+mixin genericerror()
+ h1 Oops
+ p Your request could not be processed. Status code: #{status}
, message: #{message}
+doctype html
+html(lang="en")
+ head
+ include head
+ mixin head()
+ body
+ #wrap
+ nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
+ include nav
+ mixin navheader()
+ #nav-collapsible.collapse.navbar-collapse
+ ul.nav.navbar-nav
+ mixin navdefaultlinks(path)
+ mixin navloginlogout(path)
+
+ section#mainpage.container
+ .col-md-12
+ .alert.alert-danger
+ if status == 404
+ mixin notfound()
+ else if status == 403
+ mixin forbidden()
+ else
+ mixin genericerror()
+
+ include footer
+ mixin footer()