Implement favicon and embbed icon

Thanks to Shydale for lending me their OC checkbox.
She a cute.
This commit is contained in:
Wolvan 2022-02-02 21:42:18 +01:00
parent 8042cfb4ff
commit f68ff6dbd2
11 changed files with 3715 additions and 75 deletions

View file

@ -1,3 +1,4 @@
dist/ dist/
node_modules/ node_modules/
utils/todo-finder.js utils/todo-finder.js
utils/create-favicons.js

3
.gitignore vendored
View file

@ -117,3 +117,6 @@ data/
# Config files # Config files
config*.js* config*.js*
!config.example.json !config.example.json
# Favicons that are being auto-generated
frontend/favicons/

View file

@ -10,7 +10,7 @@
<meta property="og:title" content="{{ TITLE }}" /> <meta property="og:title" content="{{ TITLE }}" />
<meta property="og:description" content="Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!" /> <meta property="og:description" content="Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!" />
<meta property="og:url" content="{{ CANONICAL_HOST }}" /> <meta property="og:url" content="{{ CANONICAL_HOST }}" />
<meta property="og:image" content="" /> <meta property="og:image" content="{{ HOST }}/static/img/icon.png" />
<meta property="og:site_name" content="Poll.Horse" /> <meta property="og:site_name" content="Poll.Horse" />
<meta content="#FFD756" data-react-helmet="true" name="theme-color" /> <meta content="#FFD756" data-react-helmet="true" name="theme-color" />
@ -27,6 +27,9 @@
const MAX_CHARACTER_LENGTH = "{{ MAX_CHARACTER_LENGTH }}"; const MAX_CHARACTER_LENGTH = "{{ MAX_CHARACTER_LENGTH }}";
</script> </script>
<script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/js/index.js" defer="true" async="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/js/index.js" defer="true" async="true"></script>
<!-- FAVICON_MARKER -->
<!-- /FAVICON_MARKER -->
</head> </head>
<body> <body>
<header> <header>

View file

@ -10,7 +10,7 @@
<meta property="og:title" content="{{ TITLE }}" /> <meta property="og:title" content="{{ TITLE }}" />
<meta property="og:description" content="{{ POLL_META_DESCRIPTION }}" /> <meta property="og:description" content="{{ POLL_META_DESCRIPTION }}" />
<meta property="og:url" content="{{ CANONICAL_HOST }}" /> <meta property="og:url" content="{{ CANONICAL_HOST }}" />
<meta property="og:image" content="" /> <meta property="og:image" content="{{ HOST }}/static/img/icon.png" />
<meta property="og:site_name" content="Poll.Horse" /> <meta property="og:site_name" content="Poll.Horse" />
<meta content="#FFD756" data-react-helmet="true" name="theme-color" /> <meta content="#FFD756" data-react-helmet="true" name="theme-color" />
@ -36,6 +36,9 @@
document.querySelectorAll(".poll-option .text").forEach(element => textFit(element, textFitOptions)); document.querySelectorAll(".poll-option .text").forEach(element => textFit(element, textFitOptions));
}); });
</script> </script>
<!-- FAVICON_MARKER -->
<!-- /FAVICON_MARKER -->
</head> </head>
<body> <body>
<header> <header>

View file

@ -10,7 +10,7 @@
<meta property="og:title" content="{{ TITLE }}" /> <meta property="og:title" content="{{ TITLE }}" />
<meta property="og:description" content="{{ POLL_META_DESCRIPTION }}" /> <meta property="og:description" content="{{ POLL_META_DESCRIPTION }}" />
<meta property="og:url" content="{{ CANONICAL_HOST }}" /> <meta property="og:url" content="{{ CANONICAL_HOST }}" />
<meta property="og:image" content="" /> <meta property="og:image" content="{{ HOST }}/static/img/icon.png" />
<meta property="og:site_name" content="Poll.Horse" /> <meta property="og:site_name" content="Poll.Horse" />
<meta content="#FFD756" data-react-helmet="true" name="theme-color" /> <meta content="#FFD756" data-react-helmet="true" name="theme-color" />
@ -30,6 +30,9 @@
const POLL_ID = "{{ POLL_ID }}"; const POLL_ID = "{{ POLL_ID }}";
</script> </script>
<script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/js/result.js" defer="true"></script> <script nonce="{{ CORS_SCRIPT_NONCE }}" type="text/javascript" src="/static/js/result.js" defer="true"></script>
<!-- FAVICON_MARKER -->
<!-- /FAVICON_MARKER -->
</head> </head>
<body> <body>
<header> <header>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

3699
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,8 @@
"find-todo": "node utils/todo-finder.js ./src && node utils/todo-finder.js ./frontend", "find-todo": "node utils/todo-finder.js ./src && node utils/todo-finder.js ./frontend",
"mocha": "mocha", "mocha": "mocha",
"lint": "eslint .", "lint": "eslint .",
"build": "rimraf ./dist && tsc", "build:favicons": "node utils/create-favicons",
"build": "rimraf ./dist && npm run build:favicons && tsc",
"test": "npm run lint && npm run find-todo && npm run mocha", "test": "npm run lint && npm run find-todo && npm run mocha",
"debug": "ts-node-dev --inspect --respawn --clear ./src/main.ts", "debug": "ts-node-dev --inspect --respawn --clear ./src/main.ts",
"start:build": "npm test && npm run build && node ./dist/main.js", "start:build": "npm test && npm run build && node ./dist/main.js",
@ -46,6 +47,7 @@
"@types/node": "^16.11.10", "@types/node": "^16.11.10",
"@types/node-fetch": "^2.5.12", "@types/node-fetch": "^2.5.12",
"@types/node-persist": "^3.1.2", "@types/node-persist": "^3.1.2",
"@types/serve-favicon": "^2.5.3",
"@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0", "@typescript-eslint/parser": "^5.4.0",
"chai": "^4.3.4", "chai": "^4.3.4",
@ -65,10 +67,12 @@
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.17.2", "express": "^4.17.2",
"favicons": "^6.2.2",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"helmet": "^5.0.2", "helmet": "^5.0.2",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"node-fetch": "^2.6.6", "node-fetch": "^2.6.6",
"node-persist": "^3.1.0" "node-persist": "^3.1.0",
"serve-favicon": "^2.5.0"
} }
} }

View file

@ -162,7 +162,8 @@ export default function init(router: Router): void {
"QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`, "QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`,
"CANONICAL_HOST": req.protocol + "://" + (req.headers.host || "") + "/" + id + "/r", "CANONICAL_HOST": req.protocol + "://" + (req.headers.host || "") + "/" + id + "/r",
"POLL_META_DESCRIPTION": xss(poll.title || "Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!").substring(0, 150), "POLL_META_DESCRIPTION": xss(poll.title || "Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!").substring(0, 150),
"CORS_SCRIPT_NONCE": res.locals.cspNonce "CORS_SCRIPT_NONCE": res.locals.cspNonce,
"HOST": req.protocol + "://" + (req.headers.host || "")
}); });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -202,7 +203,8 @@ export default function init(router: Router): void {
"QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`, "QR_CODE": `https://chart.googleapis.com/chart?cht=qr&chs=190x190&chld=L|1&chl=${ encodeURIComponent(`${ req.protocol }://${ req.headers.host }/${ id }`) }`,
"CANONICAL_HOST": req.protocol + "://" + (req.headers.host || "") + "/" + id, "CANONICAL_HOST": req.protocol + "://" + (req.headers.host || "") + "/" + id,
"POLL_META_DESCRIPTION": xss(poll.title || "Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!").substring(0, 150), "POLL_META_DESCRIPTION": xss(poll.title || "Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!").substring(0, 150),
"CORS_SCRIPT_NONCE": res.locals.cspNonce "CORS_SCRIPT_NONCE": res.locals.cspNonce,
"HOST": req.protocol + "://" + (req.headers.host || "")
}); });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -237,7 +239,8 @@ export default function init(router: Router): void {
"MAX_POLL_OPTIONS": MAX_POLL_OPTIONS, "MAX_POLL_OPTIONS": MAX_POLL_OPTIONS,
"MAX_CHARACTER_LENGTH": MAX_CHARACTER_LENGTH, "MAX_CHARACTER_LENGTH": MAX_CHARACTER_LENGTH,
"CANONICAL_HOST": req.protocol + "://" + (req.headers.host || ""), "CANONICAL_HOST": req.protocol + "://" + (req.headers.host || ""),
"CORS_SCRIPT_NONCE": res.locals.cspNonce "CORS_SCRIPT_NONCE": res.locals.cspNonce,
"HOST": req.protocol + "://" + (req.headers.host || "")
}); });
}); });
} }

View file

@ -11,6 +11,8 @@ import Storage from "./Storage";
import MySQLStorage from "./MySQLStorage"; import MySQLStorage from "./MySQLStorage";
import helmet from "helmet"; import helmet from "helmet";
import crypto from "crypto"; import crypto from "crypto";
import { statSync } from "fs";
import serveFavicon from "serve-favicon";
async function main(): Promise<void> { async function main(): Promise<void> {
await loadConfig([ await loadConfig([
@ -31,6 +33,16 @@ async function main(): Promise<void> {
const opts = program.opts(); const opts = program.opts();
const app = express(); const app = express();
try {
if (statSync(resolve(__dirname, "../frontend/favicons")).isDirectory()) {
app.use("/favicons", express.static(resolve(__dirname, "../frontend/favicons")));
if (statSync(resolve(__dirname, "../frontend/favicons/favicon.ico")).isFile()) app.use(serveFavicon(resolve(__dirname, "../frontend/favicons/favicon.ico")));
}
} catch (error) {
console.warn("FavIcons not available: ", error);
}
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }));
app.use(compression()); app.use(compression());

43
utils/create-favicons.js Normal file
View file

@ -0,0 +1,43 @@
#!/usr/bin/env node
"use strict";
const favicons = require("favicons");
const resolve = require("path").resolve;
const fs = require("fs-extra");
const start = new Date();
favicons(resolve(__dirname, "../frontend/static/img/icon.png"), {
path: "/favicons",
appName: "Poll.Horse",
appShortName: "poll_horse",
appDescription: "Simple, free and open source way to host polls for people to vote on. Create your own polls and share them with others!",
developerName: "Wolvan",
developerURL: "https://github.com/wolvan",
background: "#FFD756",
theme_color: "#FFD756",
version: require(resolve(__dirname, "../package.json")).version
}, async (error, response) => {
if (error) throw new error;
await fs.remove(resolve(__dirname, "../frontend/favicons"));
await fs.ensureDir(resolve(__dirname, "../frontend/favicons"));
await Promise.all(response.images.map(({ name, contents }) => fs.writeFile(resolve(__dirname, "../frontend/favicons", name), contents)));
await Promise.all(response.files.map(({ name, contents }) => fs.writeFile(resolve(__dirname, "../frontend/favicons", name), contents)));
try {
if ((await fs.stat(resolve(__dirname, "../frontend/html"))).isDirectory()) {
const files = await fs.readdir(resolve(__dirname, "../frontend/html"));
await Promise.all(files.filter(file => file.match(/\.html?/i)).map(async file => {
let content = await fs.readFile(resolve(__dirname, "../frontend/html", file), "utf8");
content = content
.replace(/<!-- FAVICON_MARKER -->[\s\S]*<!-- \/FAVICON_MARKER -->/i,
`<!-- FAVICON_MARKER -->
${response.html.join("\n ")}
<!-- \/FAVICON_MARKER -->`);
await fs.writeFile(resolve(__dirname, "../frontend/html", file), content);
}));
}
} catch (error) {
throw error;
}
console.log("Finished Favicon generation in", ((new Date() - start) / 1000).toFixed(2), "s");
});