[Status Page] SSR

This commit is contained in:
Louis Lam 2022-05-30 15:45:44 +08:00
parent ffb7ba176c
commit c095767f4a
6 changed files with 92 additions and 29 deletions

4
package-lock.json generated
View File

@ -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",

View File

@ -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();
}

View File

@ -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;

View File

@ -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);
}
});

View File

@ -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);
}

View File

@ -98,7 +98,7 @@
<h1 class="mb-4 title-flex">
<!-- Logo -->
<span class="logo-wrapper" @click="showImageCropUploadMethod">
<img :src="logoURL" alt class="logo me-2" :class="logoClass" @load="statusPageLogoLoaded" />
<img :src="logoURL" alt class="logo me-2" :class="logoClass" />
<font-awesome-icon v-if="enableEditMode" class="icon-upload" icon="upload" />
</span>
@ -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;