From c095767f4a5ecda103a58ac9023596b7f286209f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 30 May 2022 15:45:44 +0800 Subject: [PATCH] [Status Page] SSR --- package-lock.json | 4 ++-- server/model/status_page.js | 34 ++++++++++++++++++++++++++++ server/routers/status-page-router.js | 26 +++++++++++++++++++++ server/server.js | 34 +++++++++++----------------- server/uptime-kuma-server.js | 16 +++++++++++++ src/pages/StatusPage.vue | 7 +----- 6 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 server/routers/status-page-router.js diff --git a/package-lock.json b/package-lock.json index 86d37100f..2128ce4a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.15.1", + "version": "1.16.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.15.1", + "version": "1.16.1", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "~1.2.36", diff --git a/server/model/status_page.js b/server/model/status_page.js index 80c22d463..6b3fcc81d 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -1,11 +1,34 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); const { R } = require("redbean-node"); const cheerio = require("cheerio"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); class StatusPage extends BeanModel { + /** + * Like this: { "test-uptime.kuma.pet": "default" } + * @type {{}} + */ static domainMappingList = { }; + /** + * + * @param {Response} response + * @param {string} indexHTML + * @param {string} slug + */ + static async handleStatusPageResponse(response, indexHTML, slug) { + let statusPage = await R.findOne("status_page", " slug = ? ", [ + slug + ]); + + if (statusPage) { + response.send(StatusPage.renderHTML(indexHTML, statusPage)); + } else { + response.status(404).send(UptimeKumaServer.getInstance().indexHTML); + } + } + /** * SSR for status pages * @param {string} indexHTML @@ -14,6 +37,17 @@ class StatusPage extends BeanModel { static renderHTML(indexHTML, statusPage) { const $ = cheerio.load(indexHTML); + $("title").text(statusPage.title); + $("meta[name=description]").attr("content", statusPage.description.substring(0, 155)); + + if (statusPage.icon) { + $("link[rel=icon]") + .attr("href", statusPage.icon) + .removeAttr("type"); + } + + const head = $("head"); + return $.root().html(); } diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js new file mode 100644 index 000000000..77b791136 --- /dev/null +++ b/server/routers/status-page-router.js @@ -0,0 +1,26 @@ +let express = require("express"); +const apicache = require("../modules/apicache"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const StatusPage = require("../model/status_page"); + +let router = express.Router(); + +let cache = apicache.middleware; +const server = UptimeKumaServer.getInstance(); + +router.get("/status/:slug", cache("5 minutes"), async (request, response) => { + let slug = request.params.slug; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); +}); + +router.get("/status", cache("5 minutes"), async (request, response) => { + let slug = "default"; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); +}); + +router.get("/status-page", cache("5 minutes"), async (request, response) => { + let slug = "default"; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); +}); + +module.exports = router; diff --git a/server/server.js b/server/server.js index 328104bf5..7ee09539a 100644 --- a/server/server.js +++ b/server/server.js @@ -124,6 +124,7 @@ const TwoFA = require("./2fa"); const StatusPage = require("./model/status_page"); const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart, stop: cloudflaredStop } = require("./socket-handlers/cloudflared-socket-handler"); const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler"); +const apiRouter = require("./routers/api-router"); app.use(express.json()); @@ -148,22 +149,6 @@ let jwtSecret = null; */ let needSetup = false; -/** - * Cache Index HTML - * @type {string} - */ -let indexHTML = ""; - -try { - indexHTML = fs.readFileSync("./dist/index.html").toString(); -} catch (e) { - // "dist/index.html" is not necessary for development - if (process.env.NODE_ENV !== "development") { - log.error("server", "Error: Cannot find 'dist/index.html', did you install correctly?"); - process.exit(1); - } -} - (async () => { Database.init(args); await initDatabase(testMode); @@ -179,14 +164,17 @@ try { // Entry Page app.get("/", async (request, response) => { - debug(`Request Domain: ${request.hostname}`); + log.debug("entry", `Request Domain: ${request.hostname}`); if (request.hostname in StatusPage.domainMappingList) { - debug("This is a status page domain"); - // TODO - response.send(StatusPage.renderHTML(indexHTML)); + log.debug("entry", "This is a status page domain"); + + let slug = StatusPage.domainMappingList[request.hostname]; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); + } else if (exports.entryPage && exports.entryPage.startsWith("statusPage-")) { response.redirect("/status/" + exports.entryPage.replace("statusPage-", "")); + } else { response.redirect("/dashboard"); } @@ -228,12 +216,16 @@ try { const apiRouter = require("./routers/api-router"); app.use(apiRouter); + // Status Page Router + const statusPageRouter = require("./routers/status-page-router"); + app.use(statusPageRouter); + // Universal Route Handler, must be at the end of all express routes. app.get("*", async (_request, response) => { if (_request.originalUrl.startsWith("/upload/")) { response.status(404).send("File not found."); } else { - response.send(indexHTML); + response.send(server.indexHTML); } }); diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index d0c968e73..605ba5335 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -29,6 +29,12 @@ class UptimeKumaServer { httpServer = undefined; io = undefined; + /** + * Cache Index HTML + * @type {string} + */ + indexHTML = ""; + static getInstance(args) { if (UptimeKumaServer.instance == null) { UptimeKumaServer.instance = new UptimeKumaServer(args); @@ -55,6 +61,16 @@ class UptimeKumaServer { this.httpServer = http.createServer(this.app); } + try { + this.indexHTML = fs.readFileSync("./dist/index.html").toString(); + } catch (e) { + // "dist/index.html" is not necessary for development + if (process.env.NODE_ENV !== "development") { + log.error("server", "Error: Cannot find 'dist/index.html', did you install correctly?"); + process.exit(1); + } + } + this.io = new Server(this.httpServer); } diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 22608116d..c75022531 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -98,7 +98,7 @@

- + @@ -687,11 +687,6 @@ export default { } }, - statusPageLogoLoaded(eventPayload) { - // Remark: may not work in dev, due to CORS - favicon.image(eventPayload.target); - }, - createIncident() { this.enableEditIncidentMode = true;