diff --git a/db/patch-status-page.sql b/db/patch-status-page.sql index 07eacc023..584eb615c 100644 --- a/db/patch-status-page.sql +++ b/db/patch-status-page.sql @@ -10,7 +10,9 @@ CREATE TABLE [status_page]( [published] BOOLEAN NOT NULL DEFAULT 1, [search_engine_index] BOOLEAN NOT NULL DEFAULT 1, [show_tags] BOOLEAN NOT NULL DEFAULT 0, - [password] VARCHAR + [password] VARCHAR, + [date_created] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + [date_modified] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE UNIQUE INDEX [slug] ON [status_page]([slug]); diff --git a/server/model/status_page.js b/server/model/status_page.js new file mode 100644 index 000000000..eab89d4a9 --- /dev/null +++ b/server/model/status_page.js @@ -0,0 +1,44 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { R } = require("redbean-node"); + +class StatusPage extends BeanModel { + + static async sendStatusPageList(io, socket) { + let result = {}; + + let list = await R.findAll("status_page", " ORDER BY title "); + + for (let item of list) { + result[item.id] = await item.toJSON(); + } + + io.to(socket.userID).emit("statusPageList", result); + return list; + } + + async toJSON() { + return { + id: this.id, + slug: this.slug, + title: this.title, + icon: this.icon, + theme: this.theme, + published: !!this.published, + showTags: !!this.show_tags, + }; + } + + async toPublicJSON() { + return { + slug: this.slug, + title: this.title, + icon: this.icon, + theme: this.theme, + published: !!this.published, + showTags: !!this.show_tags, + }; + } + +} + +module.exports = StatusPage; diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1920cef71..0b8a46b40 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -83,33 +83,28 @@ router.get("/api/push/:pushToken", async (request, response) => { }); // Status Page Config -router.get("/api/status-page/config", async (_request, response) => { +router.get("/api/status-page/config/:slug", async (request, response) => { allowDevAllOrigin(response); + let slug = request.params.slug; - let config = await getSettings("statusPage"); + let statusPage = await R.findOne("status_page", " slug = ? ", [ + slug + ]); - if (! config.statusPageTheme) { - config.statusPageTheme = "light"; - } - - if (! config.statusPagePublished) { - config.statusPagePublished = true; - } - - if (! config.statusPageTags) { - config.statusPageTags = false; - } - - if (! config.title) { - config.title = "Uptime Kuma"; + if (!statusPage) { + response.statusCode = 404; + response.json({ + msg: "Not Found" + }); + return; } - response.json(config); + response.json(await statusPage.toPublicJSON()); }); // Status Page - Get the current Incident // Can fetch only if published -router.get("/api/status-page/incident", async (_, response) => { +router.get("/api/status-page/incident/:slug", async (_, response) => { allowDevAllOrigin(response); try { @@ -133,7 +128,7 @@ router.get("/api/status-page/incident", async (_, response) => { // Status Page - Monitor List // Can fetch only if published -router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request, response) => { +router.get("/api/status-page/monitor-list/:slug", cache("5 minutes"), async (_request, response) => { allowDevAllOrigin(response); try { @@ -172,7 +167,7 @@ router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request, // Status Page Polling Data // Can fetch only if published -router.get("/api/status-page/heartbeat", cache("5 minutes"), async (_request, response) => { +router.get("/api/status-page/heartbeat/:slug", cache("5 minutes"), async (_request, response) => { allowDevAllOrigin(response); try { diff --git a/server/server.js b/server/server.js index 153cac4fd..043d85bb7 100644 --- a/server/server.js +++ b/server/server.js @@ -132,6 +132,7 @@ const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sen const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const TwoFA = require("./2fa"); +const StatusPage = require("./model/status_page"); app.use(express.json()); @@ -1414,6 +1415,8 @@ async function afterLogin(socket, user) { for (let monitorID in monitorList) { await Monitor.sendStats(io, monitorID, user.id); } + + await StatusPage.sendStatusPageList(io, socket); } async function getMonitorJSONList(userID) { diff --git a/src/assets/app.scss b/src/assets/app.scss index f49ed4f2c..08f647b45 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -92,6 +92,10 @@ textarea.form-control { } } +.btn-dark { + background-color: #161B22; +} + @media (max-width: 550px) { .table-shadow-box { padding: 10px !important; @@ -162,12 +166,12 @@ textarea.form-control { .form-check-input:checked { border-color: $primary; // Re-apply bootstrap border } - + .form-switch .form-check-input { background-color: #232f3b; } - a, + a:not(.btn), .table, .nav-link { color: $dark-font-color; diff --git a/src/icon.js b/src/icon.js index 88b8a8ecd..865897608 100644 --- a/src/icon.js +++ b/src/icon.js @@ -34,6 +34,8 @@ import { faAward, faLink, faChevronDown, + faPen, + faExternalLinkSquareAlt, } from "@fortawesome/free-solid-svg-icons"; library.add( @@ -67,6 +69,8 @@ library.add( faAward, faLink, faChevronDown, + faPen, + faExternalLinkSquareAlt, ); export { FontAwesomeIcon }; diff --git a/src/languages/en.js b/src/languages/en.js index 480e97138..d66d3188d 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -183,7 +183,7 @@ export default { "Edit Status Page": "Edit Status Page", "Go to Dashboard": "Go to Dashboard", "Status Page": "Status Page", - "Status Pages": "Status Page", + "Status Pages": "Status Pages", defaultNotificationName: "My {notification} Alert ({number})", here: "here", Required: "Required", diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index 1a769a0dc..f1f915a67 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -19,9 +19,9 @@