diff --git a/dockerfile b/dockerfile index 0388d4bc..923d8b10 100644 --- a/dockerfile +++ b/dockerfile @@ -31,6 +31,7 @@ RUN npm run build EXPOSE 3001 VOLUME ["/app/data"] +HEALTHCHECK --interval=5s --timeout=3s --start-period=30s CMD node extra/healthcheck.js CMD ["npm", "run", "start-server"] FROM release AS nightly diff --git a/extra/healthcheck.js b/extra/healthcheck.js new file mode 100644 index 00000000..b547fbcb --- /dev/null +++ b/extra/healthcheck.js @@ -0,0 +1,19 @@ +var http = require("http"); +var options = { + host: "localhost", + port: "3001", + timeout: 2000, +}; +var request = http.request(options, (res) => { + console.log(`STATUS: ${res.statusCode}`); + if (res.statusCode == 200) { + process.exit(0); + } else { + process.exit(1); + } +}); +request.on("error", function (err) { + console.log("ERROR"); + process.exit(1); +}); +request.end(); diff --git a/index.html b/index.html index 3dd55d3f..66d58c1e 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,8 @@ <link rel="icon" type="image/svg+xml" href="/icon.svg" /> <link rel="apple-touch-icon" href="/apple-touch-icon.png"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="theme-color" content="#5cdd8b" /> + <meta name="description" content="Uptime Kuma monitoring tool" /> <title>Uptime Kuma</title> </head> <body> diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index db02af7b..3bb82e4c 100644 Binary files a/public/apple-touch-icon.png and b/public/apple-touch-icon.png differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/server/notification.js b/server/notification.js index 99cc1568..e264f716 100644 --- a/server/notification.js +++ b/server/notification.js @@ -15,7 +15,7 @@ class Notification { }) return true; } catch (error) { - console.log(error) + console.error(error) return false; } @@ -31,7 +31,7 @@ class Notification { }) return true; } catch (error) { - console.log(error) + console.error(error) return false; } @@ -61,7 +61,7 @@ class Notification { let res = await axios.post(notification.webhookURL, finalData, config) return true; } catch (error) { - console.log(error) + console.error(error) return false; } @@ -105,7 +105,7 @@ class Notification { let res = await axios.post(notification.discordWebhookUrl, data) return true; } catch(error) { - console.log(error) + console.error(error) return false; } @@ -121,20 +121,24 @@ class Notification { let res = await axios.post(notification.signalURL, data, config) return true; } catch (error) { - console.log(error) + console.error(error) return false; } - + } else if (notification.type === "slack") { try { if (heartbeatJSON == null) { - let data = {'text': "Uptime Kuma Slack testing successful."} + let data = {'text': "Uptime Kuma Slack testing successful.", 'channel': notification.slackchannel, 'username': notification.slackusername, 'icon_emoji': notification.slackiconemo} let res = await axios.post(notification.slackwebhookURL, data) return true; } const time = heartbeatJSON["time"]; let data = { + "text": "Uptime Kuma Alert", + "channel":notification.slackchannel, + "username": notification.slackusername, + "icon_emoji": notification.slackiconemo, "blocks": [{ "type": "header", "text": { @@ -155,23 +159,52 @@ class Notification { ] }, { - "type": "actions", - "elements": [ - { - "type": "button", - "text": { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { "type": "plain_text", "text": "Visit Uptime Kuma", }, - "value": "Uptime-Kuma", - "url": notification.slackbutton - } + "value": "Uptime-Kuma", + "url": notification.slackbutton || "https://github.com/louislam/uptime-kuma" + } ] } ] } let res = await axios.post(notification.slackwebhookURL, data) return true; + } catch (error) { + console.error(error) + return false; + } + + } else if (notification.type === "pushover") { + var pushoverlink = 'https://api.pushover.net/1/messages.json' + try { + if (heartbeatJSON == null) { + let data = {'message': "<b>Uptime Kuma Pushover testing successful.</b>", + 'user': notification.pushoveruserkey, 'token': notification.pushoverapptoken, 'sound':notification.pushoversounds, + 'priority': notification.pushoverpriority, 'title':notification.pushovertitle, 'retry': "30", 'expire':"3600", 'html': 1} + let res = await axios.post(pushoverlink, data) + return true; + } + + let data = { + "message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" +msg + '\n<b>Time (UTC)</b>:' +time, + "user":notification.pushoveruserkey, + "token": notification.pushoverapptoken, + "sound": notification.pushoversounds, + "priority": notification.pushoverpriority, + "title": notification.pushovertitle, + "retry": "30", + "expire": "3600", + "html": 1 + } + let res = await axios.post(pushoverlink, data) + return true; } catch (error) { console.log(error) return false; diff --git a/server/server.js b/server/server.js index 93e43cd6..b53f32b4 100644 --- a/server/server.js +++ b/server/server.js @@ -19,7 +19,7 @@ const version = require('../package.json').version; const hostname = args.host || "0.0.0.0" const port = args.port || 3001 -console.log("Version: " + version) +console.info("Version: " + version) console.log("Creating express and socket.io instance") const app = express(); @@ -236,7 +236,7 @@ let needSetup = false; }); } catch (e) { - console.log(e) + console.error(e) callback({ ok: false, msg: e.message diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 15991517..c5be0c65 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -20,6 +20,7 @@ <option value="signal">Signal</option> <option value="gotify">Gotify</option> <option value="slack">Slack</option> + <option value="pushover">Pushover</option> </select> </div> @@ -191,15 +192,80 @@ <template v-if="notification.type === 'slack'"> <div class="mb-3"> - <label for="slack-webhook-url" class="form-label">Slack Webhook URL</label> - <input type="text" class="form-control" id="slack-webhook-url" required v-model="notification.slackwebhookURL" autocomplete="false"> - <label for="gotify-server-url" class="form-label">Uptime Kuma URL</label> - <div class="input-group mb-3"> - <input type="text" class="form-control" id="slack-button" required v-model="notification.slackbutton" autocomplete="false"> - </div> + <label for="slack-webhook-url" class="form-label">Webhook URL<span style="color:red;"><sup>*</sup></span></label> + <input type="text" class="form-control" id="slack-webhook-url" required v-model="notification.slackwebhookURL"> + <label for="slack-username" class="form-label">Username</label> + <input type="text" class="form-control" id="slack-username" v-model="notification.slackusername"> + <label for="slack-iconemo" class="form-label">Icon Emoji</label> + <input type="text" class="form-control" id="slack-iconemo" v-model="notification.slackiconemo"> + <label for="slack-channel" class="form-label">Channel Name</label> + <input type="text" class="form-control" id="slack-channel-name" v-model="notification.slackchannel"> + <label for="slack-button-url" class="form-label">Uptime Kuma URL</label> + <input type="text" class="form-control" id="slack-button" v-model="notification.slackbutton"> + <div class="form-text"> + <span style="color:red;"><sup>*</sup></span>Required + <p style="margin-top: 8px;"> + More info about webhooks on: <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a> + </p> + <p style="margin-top: 8px;"> + Enter the channel name on Slack Channel Name field if you want to bypass the webhook channel. Ex: #other-channel + </p> + <p style="margin-top: 8px;"> + If you leave the Uptime Kuma URL field blank, it will default to the Project Github page. + </p> + <p style="margin-top: 8px;"> + Emoji cheat sheet: <a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a> + </p> + </div> + </div> + </template> + + <template v-if="notification.type === 'pushover'"> + <div class="mb-3"> + <label for="pushover-app-token" class="form-label">Application Token<span style="color:red;"><sup>*</sup></span></label> + <input type="text" class="form-control" id="pushover-app-token" required v-model="notification.pushoverapptoken"> + <label for="pushover-user" class="form-label">User Key<span style="color:red;"><sup>*</sup></span></label> + <input type="text" class="form-control" id="pushover-user" required v-model="notification.pushoveruserkey"> + <label for="pushover-device" class="form-label">Device</label> + <input type="text" class="form-control" id="pushover-device" v-model="notification.pushoverdevice"> + <label for="pushover-device" class="form-label">Message Title</label> + <input type="text" class="form-control" id="pushover-title" v-model="notification.pushovertitle"> + <label for="pushover-priority" class="form-label">Priority</label> + <input type="text" class="form-control" id="pushover-priority" v-model="notification.pushoverpriority"> + <label for="pushover-sound" class="form-label">Notification Sound</label> + <select class="form-select" id="pushover-sound" v-model="notification.pushoversounds"> + <option>pushover</option> + <option>bike</option> + <option>bugle</option> + <option>cashregister</option> + <option>classical</option> + <option>cosmic</option> + <option>falling</option> + <option>gamelan</option> + <option>incoming</option> + <option>intermission</option> + <option>mechanical</option> + <option>pianobar</option> + <option>siren</option> + <option>spacealarm</option> + <option>tugboat</option> + <option>alien</option> + <option>climb</option> + <option>persistent</option> + <option>echo</option> + <option>updown</option> + <option>vibrate</option> + <option>none</option> + </select> + <div class="form-text"> + <span style="color:red;"><sup>*</sup></span>Required <p style="margin-top: 8px;"> - More info on: <a href="https://api.slack.com/messaging/webhooks" target="_blank">https://api.slack.com/messaging/webhooks</a> + More info on: <a href="https://pushover.net/api" target="_blank">https://pushover.net/api</a> </p> + <p style="margin-top: 8px;"> + Emergency priority(2) has default 30 second timeout between retries and will expire after 1 hour. + </p> + </div> </div> </template> diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index b9aca97f..85109e12 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -9,7 +9,7 @@ <!-- Desktop header --> <header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom" v-if="! $root.isMobile"> <router-link to="/dashboard" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none"> - <object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg"></object> + <object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg" alt="Logo"></object> <span class="fs-4 title">Uptime Kuma</span> </router-link> @@ -37,7 +37,7 @@ <div class="container-fluid"> Uptime Kuma - Version: {{ $root.info.version }} - - <a href="https://github.com/louislam/uptime-kuma/releases" target="_blank">Check Update On GitHub</a> + <a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">Check Update On GitHub</a> </div> </footer> @@ -146,6 +146,7 @@ footer { font-size: 13px; margin-bottom: 30px; margin-left: 10px; + text-align: center; } </style> diff --git a/src/util-frontend.js b/src/util-frontend.js index 7094c159..abdf7b5a 100644 --- a/src/util-frontend.js +++ b/src/util-frontend.js @@ -394,7 +394,7 @@ export function timezoneList() { time: getTimezoneOffset(timezone), }) } catch (e) { - console.log(e.message); + console.error(e.message); console.log("Skip this timezone") }