Browse Source

#253: add basepath to favicon

pull/274/head
butlerx 4 years ago
parent
commit
a2f55fc4d5
No known key found for this signature in database GPG Key ID: B37CA765BAA89170
  1. 8
      package.json
  2. 2
      src/server/socketServer.ts
  3. 14
      src/server/socketServer/html.ts
  4. 100
      src/server/socketServer/middleware.ts
  5. 46
      yarn.lock

8
package.json

@ -97,16 +97,18 @@
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"compression": "^1.7.4",
"etag": "^1.8.1",
"express": "^4.17.1",
"express-winston": "^4.0.5",
"file-type": "^12.3.0",
"fresh": "^0.5.2",
"fs-extra": "^9.0.1",
"helmet": "^4.1.0",
"json5": "^2.1.3",
"lodash": "^4.17.20",
"node-pty": "^0.9.0",
"parseurl": "^1.3.3",
"sass": "^1.26.10",
"serve-favicon": "^2.5.0",
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0",
"toastify-js": "^1.9.1",
@ -118,7 +120,9 @@
"devDependencies": {
"@types/chai": "^4.2.12",
"@types/compression": "^1.7.0",
"@types/etag": "^1.8.0",
"@types/express": "^4.17.8",
"@types/fresh": "^0.5.0",
"@types/fs-extra": "^9.0.1",
"@types/helmet": "^0.0.48",
"@types/jsdom": "^12.2.4",
@ -126,7 +130,7 @@
"@types/mocha": "^8.0.3",
"@types/morgan": "^1.7.37",
"@types/node": "^14.6.3",
"@types/serve-favicon": "^2.5.0",
"@types/parseurl": "^1.3.1",
"@types/sinon": "^7.5.1",
"@types/socket.io": "^2.1.11",
"@types/socket.io-client": "^1.4.33",

2
src/server/socketServer.ts

@ -32,7 +32,7 @@ export async function server(
.use(`${basePath}/client`, serveStatic('client'))
.use(winston.logger(logger))
.use(compression())
.use(favicon)
.use(favicon(basePath))
.use(redirect)
.use(policies(allowIframe))
.get(basePath, client)

14
src/server/socketServer/html.ts

@ -1,4 +1,4 @@
import type express from 'express';
import type { Request, Response, RequestHandler } from 'express';
import { isDev } from '../../shared/env.js';
const jsFiles = isDev ? ['dev', 'wetty'] : ['wetty'];
@ -6,6 +6,7 @@ const cssFiles = ['styles', 'options', 'overlay', 'terminal'];
const render = (
title: string,
favicon: string,
css: string[],
js: string[],
): string => `<!doctype html>
@ -14,6 +15,7 @@ const render = (
<meta charset="utf8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel="icon" type="image/x-icon" href="${favicon}">
<title>${title}</title>
${css.map(file => `<link rel="stylesheet" href="${file}" />`).join('\n')}
</head>
@ -38,14 +40,16 @@ const render = (
</body>
</html>`;
export const html = (base: string, title: string) => (
_req: express.Request,
res: express.Response,
) =>
export const html = (base: string, title: string): RequestHandler => (
_req: Request,
res: Response,
): void => {
res.send(
render(
title,
`${base}/favicon.ico`,
cssFiles.map(css => `${base}/assets/css/${css}.css`),
jsFiles.map(js => `${base}/client/${js}.js`),
),
);
};

100
src/server/socketServer/middleware.ts

@ -1,15 +1,99 @@
import type express from 'express';
import { join } from 'path';
import { default as _favicon } from 'serve-favicon';
import type { Request, Response, NextFunction, RequestHandler } from 'express';
import etag from 'etag';
import fresh from 'fresh';
import parseUrl from 'parseurl';
import fs from 'fs';
import { join, resolve } from 'path';
export const favicon = _favicon(join('build', 'assets', 'favicon.ico'));
const ONE_YEAR_MS = 60 * 60 * 24 * 365 * 1000; // 1 year
/**
* Determine if the cached representation is fresh.
* @param req - server request
* @param res - server response
* @returns if the cache is fresh or not
*/
const isFresh = (req: Request, res: Response): boolean =>
fresh(req.headers, {
etag: res.getHeader('ETag'),
'last-modified': res.getHeader('Last-Modified'),
});
/**
* redirect requests with trailing / to remove it
*
* @param req - server request
* @param res - server response
* @param next - next middleware to call on finish
*/
export function redirect(
req: express.Request,
res: express.Response,
next: Function,
) {
req: Request,
res: Response,
next: NextFunction,
): void {
if (req.path.substr(-1) === '/' && req.path.length > 1)
res.redirect(301, req.path.slice(0, -1) + req.url.slice(req.path.length));
else next();
}
/**
* Serves the favicon located by the given `path`.
*
* @param basePath - server base path
* @returns middleware
*/
export function favicon(basePath: string): RequestHandler {
const path = resolve(join('build', 'assets', 'favicon.ico'));
return (req: Request, res: Response, next: NextFunction): void => {
if (getPathName(req) !== `${basePath}/favicon.ico`) {
next();
return;
}
if (req.method !== 'GET' && req.method !== 'HEAD') {
res.statusCode = req.method === 'OPTIONS' ? 200 : 405;
res.setHeader('Allow', 'GET, HEAD, OPTIONS');
res.setHeader('Content-Length', '0');
res.end();
return;
}
fs.readFile(path, (err: Error | null, buf: Buffer) => {
if (err) return next(err);
Object.entries({
'Cache-Control': `public, max-age=${Math.floor(ONE_YEAR_MS / 1000)}`,
ETag: etag(buf),
}).forEach(([key, value]) => {
res.setHeader(key, value);
});
// Validate freshness
if (isFresh(req, res)) {
res.statusCode = 304;
return res.end();
}
// Send icon
res.statusCode = 200;
res.setHeader('Content-Length', buf.length);
res.setHeader('Content-Type', 'image/x-icon');
return res.end(buf);
});
};
}
/**
* Get the request pathname.
*
* @param requests
* @returns path name or undefined
*/
function getPathName(req: Request): string | undefined {
try {
const url = parseUrl(req);
return url?.pathname ? url.pathname : undefined;
} catch (e) {
return undefined;
}
}

46
yarn.lock

@ -299,6 +299,13 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
"@types/etag@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@types/etag/-/etag-1.8.0.tgz#37f0b1f3ea46da7ae319bbedb607e375b4c99f7e"
integrity sha512-EdSN0x+Y0/lBv7YAb8IU4Jgm6DWM+Bqtz7o5qozl96fzaqdqbdfHS5qjdpFeIv7xQ8jSLyjMMNShgYtMajEHyQ==
dependencies:
"@types/node" "*"
"@types/express-serve-static-core@*":
version "4.17.12"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.12.tgz#9a487da757425e4f267e7d1c5720226af7f89591"
@ -318,6 +325,11 @@
"@types/qs" "*"
"@types/serve-static" "*"
"@types/fresh@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@types/fresh/-/fresh-0.5.0.tgz#4d09231027d69c4369cfb01a9af5ef083d0d285f"
integrity sha512-eGPzuyc6wZM3sSHJdF7NM2jW6B/xsB014Rqg/iDa6xY02mlfy1w/TE2sYhR8vbHxkzJOXiGo6NuIk3xk35vsgQ==
"@types/fs-extra@^9.0.1":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918"
@ -405,6 +417,13 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/parseurl@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@types/parseurl/-/parseurl-1.3.1.tgz#e3cb1102160e48efa59f497c4ec22dee4f3b5b27"
integrity sha1-48sRAhYOSO+ln0l8TsIt7k87Wyc=
dependencies:
"@types/node" "*"
"@types/qs@*":
version "6.9.4"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a"
@ -429,13 +448,6 @@
dependencies:
"@types/node" "*"
"@types/serve-favicon@^2.5.0":
version "2.5.0"
resolved "https://registry.yarnpkg.com/@types/serve-favicon/-/serve-favicon-2.5.0.tgz#21164e61290d577d75e22de1b3119fad70bf52b6"
integrity sha512-APK6i1tJp8XBYCZyU4HqtNZBiwipIBQvpQVLYZezTm4TaKKl0KrsGokQK9k3Ll2CaEGNuehppKhXp/Ki9oWT/w==
dependencies:
"@types/express" "*"
"@types/serve-static@*":
version "1.13.5"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53"
@ -2398,7 +2410,7 @@ forwarded@~0.1.2:
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
fresh@0.5.2:
fresh@0.5.2, fresh@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
@ -4111,7 +4123,7 @@ parseuri@0.0.5:
dependencies:
better-assert "~1.0.0"
parseurl@~1.3.2, parseurl@~1.3.3:
parseurl@^1.3.3, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
@ -4703,11 +4715,6 @@ rxjs@^6.5.2, rxjs@^6.6.0, rxjs@^6.6.2:
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@ -4795,17 +4802,6 @@ serialize-javascript@4.0.0:
dependencies:
randombytes "^2.1.0"
serve-favicon@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0"
integrity sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=
dependencies:
etag "~1.8.1"
fresh "0.5.2"
ms "2.1.1"
parseurl "~1.3.2"
safe-buffer "5.1.1"
serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"

Loading…
Cancel
Save