diff --git a/package-lock.json b/package-lock.json index fd2e20ffca..8a1d37939f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "eslint-plugin-mocha": "^5.0.0", "express": "^4.21.1", "express-basic-auth": "^1.2.1", + "express-sitemap-xml": "^3.1.0", "express-validator": "^5.2.0", "firebase-admin": "^12.1.1", "glob": "^8.1.0", @@ -10140,6 +10141,30 @@ "basic-auth": "^2.0.1" } }, + "node_modules/express-sitemap-xml": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/express-sitemap-xml/-/express-sitemap-xml-3.1.0.tgz", + "integrity": "sha512-rhm4ydngymgQlUyKor2kiY9Xf3wWWb/tbXYVMvidxyA83D1JjKOqYo23clhMvwJ+fk2ht11KtJwcaHnhhdo4PQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "p-memoize": "^4.0.1", + "xmlbuilder": "^15.1.1" + } + }, "node_modules/express-validator": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-5.3.1.tgz", @@ -14862,6 +14887,18 @@ "node": ">=0.10.0" } }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "license": "MIT", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -17227,6 +17264,15 @@ "node": ">=8" } }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-event": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-1.3.0.tgz", @@ -17306,6 +17352,32 @@ "node": ">=4" } }, + "node_modules/p-memoize": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/p-memoize/-/p-memoize-4.0.4.tgz", + "integrity": "sha512-ijdh0DP4Mk6J4FXlOM6vPPoCjPytcEseW8p/k5SDTSSfGV3E9bpt9Yzfifvzp6iohIieoLTkXRb32OWV0fB2Lw==", + "license": "MIT", + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.0.0", + "p-settle": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/p-memoize?sponsor=1" + } + }, + "node_modules/p-memoize/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-pipe": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", @@ -17326,6 +17398,31 @@ "node": ">=4" } }, + "node_modules/p-reflect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reflect/-/p-reflect-2.1.0.tgz", + "integrity": "sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-settle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/p-settle/-/p-settle-4.1.1.tgz", + "integrity": "sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.2", + "p-reflect": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-timeout": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", diff --git a/package.json b/package.json index b5f1259ebd..c024c2ee71 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "eslint-plugin-mocha": "^5.0.0", "express": "^4.21.1", "express-basic-auth": "^1.2.1", + "express-sitemap-xml": "^3.1.0", "express-validator": "^5.2.0", "firebase-admin": "^12.1.1", "glob": "^8.1.0", diff --git a/website/client/index-faq.html b/website/client/index-faq.html new file mode 100644 index 0000000000..a4bb2a4add --- /dev/null +++ b/website/client/index-faq.html @@ -0,0 +1,22 @@ + + + + + + + Habitica - FAQ + + + + + + + + + +
+ + + + + diff --git a/website/client/vite.config.mjs b/website/client/vite.config.mjs index 7288b26f99..a0576edc2b 100644 --- a/website/client/vite.config.mjs +++ b/website/client/vite.config.mjs @@ -121,6 +121,10 @@ export default defineConfig({ include: [/moment-recur/, /node_modules/] }, rollupOptions: { + input: { + main: path.resolve(__dirname, 'index.html'), + faq: path.resolve(__dirname, 'index-faq.html'), + }, output: { experimentalMinChunkSize: 20000 } diff --git a/website/server/controllers/top-level/pages.js b/website/server/controllers/top-level/pages.js index 2c3b9d7836..e825d7b581 100644 --- a/website/server/controllers/top-level/pages.js +++ b/website/server/controllers/top-level/pages.js @@ -1,9 +1,9 @@ +import nconf from 'nconf'; import { serveClient } from '../../libs/client'; -const api = {}; +const BASE_URL = nconf.get('BASE_URL'); -// All requests to /new_app (except /new_app/static) should serve the new client in development -// if (IS_PROD && IS_NEW_CLIENT_ENABLED) { +const api = {}; // All the routes (except for the api and payments routes) serve the new client side // The code that does it can be found in /middlewares/notFound.js @@ -16,4 +16,22 @@ api.getNewClient = { }, }; +api.getFAQEntryPoint = { + method: 'GET', + url: '/static/faq', + noLanguage: true, + async handler (req, res) { + return serveClient(res, 'index-faq.html'); + }, +}; + +api.robotsTxt = { + method: 'GET', + url: '/robots.txt', + noLanguage: true, + async handler (req, res) { + res.type('text/plain'); + res.send(`User-agent: *\nAllow: /\nSitemap: ${BASE_URL}/sitemap.xml`); + }, +}; export default api; diff --git a/website/server/libs/client.js b/website/server/libs/client.js index c057e2716c..d6d6c27c28 100644 --- a/website/server/libs/client.js +++ b/website/server/libs/client.js @@ -2,6 +2,6 @@ const ROOT = `${__dirname}/../../../`; const TEN_MINUTES = 1000 * 60 * 10; -export function serveClient (expressRes) { // eslint-disable-line import/prefer-default-export - return expressRes.sendFile('./website/client/dist/index.html', { root: ROOT, maxAge: TEN_MINUTES }); +export function serveClient (expressRes, file = 'index.html') { // eslint-disable-line import/prefer-default-export + return expressRes.sendFile(`./website/client/dist/${file}`, { root: ROOT, maxAge: TEN_MINUTES }); } diff --git a/website/server/middlewares/index.js b/website/server/middlewares/index.js index 4a0610a964..7f439d1428 100644 --- a/website/server/middlewares/index.js +++ b/website/server/middlewares/index.js @@ -35,6 +35,7 @@ import { logRequestData, logSlowRequests, } from './requestLogHandler'; +import sitemap from './sitemap'; const IS_PROD = nconf.get('IS_PROD'); const DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING') === 'true'; @@ -74,6 +75,8 @@ export default function attachMiddlewares (app, server) { referrerPolicy: false, })); + app.use(sitemap); + // add res.respond and res.t app.use(responseHandler); app.use(attachTranslateFunction); diff --git a/website/server/middlewares/sitemap.js b/website/server/middlewares/sitemap.js new file mode 100644 index 0000000000..9ec9eb9623 --- /dev/null +++ b/website/server/middlewares/sitemap.js @@ -0,0 +1,24 @@ +import expressSitemapXml from 'express-sitemap-xml'; +import nconf from 'nconf'; + +const BASE_URL = nconf.get('BASE_URL'); + +function makeSitemapUrls () { + return [ + '/', + '/static/login', + '/static/register', + '/stati/community-guidelines', + '/static/contact', + '/static/faq', + '/static/features', + '/static/group-plans', + '/static/news', + '/static/overview', + '/static/press-kit', + '/static/privacy', + '/static/terms', + ]; +} + +export default expressSitemapXml(makeSitemapUrls, BASE_URL);