mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-06-06 13:12:33 +02:00
Merge remote-tracking branch 'upstream/master'
# Conflicts: # docker-compose.yml # dockerfile # package-lock.json # package.json # server/notification.js # server/server.js # src/assets/vars.scss
This commit is contained in:
commit
7cabafebaf
13 changed files with 148 additions and 146 deletions
|
@ -13,3 +13,6 @@ trim_trailing_whitespace = false
|
||||||
|
|
||||||
[*.yaml]
|
[*.yaml]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_size = 2
|
||||||
|
|
BIN
db/kuma.db
BIN
db/kuma.db
Binary file not shown.
BIN
public/icon.png
Normal file
BIN
public/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -1,5 +1,5 @@
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const { R } = require("redbean-node");
|
const {R} = require("redbean-node");
|
||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const nodemailer = require("nodemailer");
|
const nodemailer = require("nodemailer");
|
||||||
|
|
||||||
|
@ -52,62 +52,64 @@ class Notification {
|
||||||
} else if (notification.type === "smtp") {
|
} else if (notification.type === "smtp") {
|
||||||
return await Notification.smtp(notification, msg)
|
return await Notification.smtp(notification, msg)
|
||||||
|
|
||||||
} else if (notification.type === "signal") {
|
|
||||||
try {
|
|
||||||
let data = {
|
|
||||||
"message": msg,
|
|
||||||
"number": notification.signalNumber,
|
|
||||||
"recipients": notification.signalRecipients.replace(/\s/g, '').split(",")
|
|
||||||
};
|
|
||||||
let config = {};
|
|
||||||
|
|
||||||
let res = await axios.post(notification.signalURL, data, config)
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (notification.type === "discord") {
|
} else if (notification.type === "discord") {
|
||||||
try {
|
try {
|
||||||
// If heartbeatJSON is null, assume we're testing.
|
// If heartbeatJSON is null, assume we're testing.
|
||||||
if (heartbeatJSON == null) {
|
if(heartbeatJSON == null) {
|
||||||
let data = {
|
|
||||||
username: 'Uptime-Kuma',
|
|
||||||
content: msg
|
|
||||||
}
|
|
||||||
let res = await axios.post(notification.discordWebhookUrl, data)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
|
||||||
if (heartbeatJSON['status'] == 0) {
|
|
||||||
var alertColor = "16711680";
|
|
||||||
} else if (heartbeatJSON['status'] == 1) {
|
|
||||||
var alertColor = "65280";
|
|
||||||
}
|
|
||||||
let data = {
|
let data = {
|
||||||
username: 'Uptime-Kuma',
|
username: 'Uptime-Kuma',
|
||||||
embeds: [{
|
content: msg
|
||||||
title: "Uptime-Kuma Alert",
|
|
||||||
color: alertColor,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: "Time (UTC)",
|
|
||||||
value: heartbeatJSON["time"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Message",
|
|
||||||
value: msg
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
let res = await axios.post(notification.discordWebhookUrl, data)
|
let res = await axios.post(notification.discordWebhookUrl, data)
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
}
|
||||||
console.log(error)
|
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
||||||
return false;
|
if(heartbeatJSON['status'] == 0) {
|
||||||
|
var alertColor = "16711680";
|
||||||
|
} else if(heartbeatJSON['status'] == 1) {
|
||||||
|
var alertColor = "65280";
|
||||||
|
}
|
||||||
|
let data = {
|
||||||
|
username: 'Uptime-Kuma',
|
||||||
|
embeds: [{
|
||||||
|
title: "Uptime-Kuma Alert",
|
||||||
|
color: alertColor,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "Time (UTC)",
|
||||||
|
value: heartbeatJSON["time"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Message",
|
||||||
|
value: msg
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
let res = await axios.post(notification.discordWebhookUrl, data)
|
||||||
|
return true;
|
||||||
|
} catch(error) {
|
||||||
|
console.log(error)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return await Notification.discord(notification, msg)
|
||||||
|
|
||||||
|
} else if (notification.type === "signal") {
|
||||||
|
try {
|
||||||
|
let data = {
|
||||||
|
"message": msg,
|
||||||
|
"number": notification.signalNumber,
|
||||||
|
"recipients": notification.signalRecipients.replace(/\s/g, '').split(",")
|
||||||
|
};
|
||||||
|
let config = {};
|
||||||
|
|
||||||
|
let res = await axios.post(notification.signalURL, data, config)
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Notification type is not supported")
|
throw new Error("Notification type is not supported")
|
||||||
}
|
}
|
||||||
|
@ -122,7 +124,7 @@ class Notification {
|
||||||
userID,
|
userID,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!bean) {
|
if (! bean) {
|
||||||
throw new Error("notification not found")
|
throw new Error("notification not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +144,7 @@ class Notification {
|
||||||
userID,
|
userID,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!bean) {
|
if (! bean) {
|
||||||
throw new Error("notification not found")
|
throw new Error("notification not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +173,18 @@ class Notification {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async discord(notification, msg) {
|
||||||
|
const client = new Discord.Client();
|
||||||
|
await client.login(notification.discordToken)
|
||||||
|
|
||||||
|
const channel = await client.channels.fetch(notification.discordChannelID);
|
||||||
|
await channel.send(msg);
|
||||||
|
|
||||||
|
client.destroy()
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -5,20 +5,18 @@ const server = http.createServer(app);
|
||||||
const { Server } = require("socket.io");
|
const { Server } = require("socket.io");
|
||||||
const io = new Server(server);
|
const io = new Server(server);
|
||||||
const dayjs = require("dayjs");
|
const dayjs = require("dayjs");
|
||||||
const { R } = require("redbean-node");
|
const {R} = require("redbean-node");
|
||||||
const passwordHash = require('password-hash');
|
const passwordHash = require('password-hash');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const Monitor = require("./model/monitor");
|
const Monitor = require("./model/monitor");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const { getSettings } = require("./util-server");
|
const {getSettings} = require("./util-server");
|
||||||
const { Notification } = require("./notification")
|
const {Notification} = require("./notification")
|
||||||
const args = require('args-parser')(process.argv);
|
const args = require('args-parser')(process.argv);
|
||||||
|
|
||||||
console.log("args:")
|
const version = require('../package.json').version;
|
||||||
console.log(args)
|
|
||||||
|
|
||||||
const hostname = args.host || "0.0.0.0"
|
const hostname = args.host || "0.0.0.0"
|
||||||
const port = args.port || 50013
|
const port = args.port || 3001
|
||||||
|
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
|
|
||||||
|
@ -32,18 +30,16 @@ let needSetup = false;
|
||||||
|
|
||||||
app.use('/', express.static("dist"));
|
app.use('/', express.static("dist"));
|
||||||
|
|
||||||
app.post('/test-webhook', function (request, response, next) {
|
app.get('*', function(request, response, next) {
|
||||||
console.log("Test Webhook (application/json only)")
|
|
||||||
console.log("Content-Type: " + request.header("Content-Type"))
|
|
||||||
console.log(request.body)
|
|
||||||
response.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('*', function (request, response, next) {
|
|
||||||
response.sendFile(process.cwd() + '/dist/index.html');
|
response.sendFile(process.cwd() + '/dist/index.html');
|
||||||
});
|
});
|
||||||
|
|
||||||
io.on('connection', async (socket) => {
|
io.on('connection', async (socket) => {
|
||||||
|
|
||||||
|
socket.emit("info", {
|
||||||
|
version,
|
||||||
|
})
|
||||||
|
|
||||||
console.log('a user connected');
|
console.log('a user connected');
|
||||||
totalClient++;
|
totalClient++;
|
||||||
|
|
||||||
|
@ -194,7 +190,7 @@ let needSetup = false;
|
||||||
try {
|
try {
|
||||||
checkLogin(socket)
|
checkLogin(socket)
|
||||||
|
|
||||||
let bean = await R.findOne("monitor", " id = ? ", [monitor.id])
|
let bean = await R.findOne("monitor", " id = ? ", [ monitor.id ])
|
||||||
|
|
||||||
if (bean.user_id !== socket.userID) {
|
if (bean.user_id !== socket.userID) {
|
||||||
throw new Error("Permission denied.")
|
throw new Error("Permission denied.")
|
||||||
|
@ -332,7 +328,7 @@ let needSetup = false;
|
||||||
try {
|
try {
|
||||||
checkLogin(socket)
|
checkLogin(socket)
|
||||||
|
|
||||||
if (!password.currentPassword) {
|
if (! password.currentPassword) {
|
||||||
throw new Error("Invalid new password")
|
throw new Error("Invalid new password")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,7 +467,7 @@ async function checkOwner(userID, monitorID) {
|
||||||
userID,
|
userID,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!row) {
|
if (! row) {
|
||||||
throw new Error("You do not own this monitor.");
|
throw new Error("You do not own this monitor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,7 +522,7 @@ async function getMonitorJSONList(userID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkLogin(socket) {
|
function checkLogin(socket) {
|
||||||
if (!socket.userID) {
|
if (! socket.userID) {
|
||||||
throw new Error("You are not logged in.");
|
throw new Error("You are not logged in.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,7 +530,7 @@ function checkLogin(socket) {
|
||||||
async function initDatabase() {
|
async function initDatabase() {
|
||||||
const path = './data/kuma.db';
|
const path = './data/kuma.db';
|
||||||
|
|
||||||
if (!fs.existsSync(path)) {
|
if (! fs.existsSync(path)) {
|
||||||
console.log("Copy Database")
|
console.log("Copy Database")
|
||||||
fs.copyFileSync("./db/kuma.db", path);
|
fs.copyFileSync("./db/kuma.db", path);
|
||||||
}
|
}
|
||||||
|
@ -551,7 +547,7 @@ async function initDatabase() {
|
||||||
"jwtSecret"
|
"jwtSecret"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!jwtSecretBean) {
|
if (! jwtSecretBean) {
|
||||||
console.log("JWT secret is not found, generate one.")
|
console.log("JWT secret is not found, generate one.")
|
||||||
jwtSecretBean = R.dispense("setting")
|
jwtSecretBean = R.dispense("setting")
|
||||||
jwtSecretBean.key = "jwtSecret"
|
jwtSecretBean.key = "jwtSecret"
|
||||||
|
@ -638,7 +634,7 @@ async function sendHeartbeatList(socket, monitorID) {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
for (let bean of list) {
|
for (let bean of list) {
|
||||||
result.unshift(bean.toJSON())
|
result.unshift(bean.toJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit("heartbeatList", monitorID, result)
|
socket.emit("heartbeatList", monitorID, result)
|
||||||
|
|
|
@ -1,46 +1,16 @@
|
||||||
@import "vars.scss";
|
@import "vars.scss";
|
||||||
@import "node_modules/bootstrap/scss/bootstrap";
|
@import "node_modules/bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
html,
|
|
||||||
body,
|
|
||||||
input,
|
|
||||||
.modal-content {
|
|
||||||
background: var(--page-background);
|
|
||||||
color: var(--main-font-color);
|
|
||||||
}
|
|
||||||
a,
|
|
||||||
.table,
|
|
||||||
.nav-link {
|
|
||||||
color: var(--main-font-color);
|
|
||||||
}
|
|
||||||
.nav-pills .nav-link.active,
|
|
||||||
.nav-pills .show > .nav-link {
|
|
||||||
color: #0a0a0a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover,
|
|
||||||
.nav-link:focus {
|
|
||||||
color: #5cdd8b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control,
|
|
||||||
.form-control:focus,
|
|
||||||
.form-select,
|
|
||||||
.form-select:focus {
|
|
||||||
color: var(--main-font-color);
|
|
||||||
background-color: var(--background-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji;
|
||||||
segoe ui, Roboto, helvetica neue, Arial, noto sans, sans-serif,
|
|
||||||
apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow-box {
|
.shadow-box {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 15px 70px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 15px 70px rgba(0, 0, 0, .1);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
&.big-padding {
|
&.big-padding {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
@ -52,14 +22,10 @@ a,
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
// color: white;
|
color: white;
|
||||||
color: #0a0a0a;
|
|
||||||
|
|
||||||
&:hover,
|
&:hover, &:active, &:focus, &.active {
|
||||||
&:active,
|
color: white;
|
||||||
&:focus,
|
|
||||||
&.active {
|
|
||||||
color: #0a0a0a;
|
|
||||||
background-color: $highlight;
|
background-color: $highlight;
|
||||||
border-color: $highlight;
|
border-color: $highlight;
|
||||||
}
|
}
|
||||||
|
@ -70,8 +36,3 @@ a,
|
||||||
backdrop-filter: blur(3px);
|
backdrop-filter: blur(3px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
a:hover {
|
|
||||||
color: #7ce8a4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ export default {
|
||||||
border-radius: 50rem;
|
border-radius: 50rem;
|
||||||
|
|
||||||
&.empty {
|
&.empty {
|
||||||
background-color: var(--background-ternary);
|
background-color: aliceblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.down {
|
&.down {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<span class="badge rounded-pill" :class="'bg-' + color">{{ text }}</span>
|
<span class="badge rounded-pill" :class=" 'bg-' + color ">{{ text }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -33,10 +33,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
span {
|
span {
|
||||||
width: 45px;
|
width: 45px;
|
||||||
}
|
}
|
||||||
.badge {
|
|
||||||
color: #0a0a0a;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -57,7 +57,5 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.badge{
|
|
||||||
color: #0a0a0a;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
<!-- Desktop header -->
|
<!-- Desktop header -->
|
||||||
<header class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom" v-if="! $root.isMobile">
|
<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-decoration-none">
|
<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"></object>
|
||||||
<span class="fs-4 title">Uptime Kuma</span>
|
<span class="fs-4 title">Uptime Kuma</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<!-- Mobile header -->
|
<!-- Mobile header -->
|
||||||
<header class="d-flex flex-wrap justify-content-center mt-3 mb-3" v-else>
|
<header class="d-flex flex-wrap justify-content-center mt-3 mb-3" v-else>
|
||||||
<router-link to="/dashboard" class="d-flex align-items-center text-decoration-none">
|
<router-link to="/dashboard" class="d-flex align-items-center text-dark text-decoration-none">
|
||||||
<object class="bi" width="40" height="40" data="/icon.svg"></object>
|
<object class="bi" width="40" height="40" data="/icon.svg"></object>
|
||||||
<span class="fs-4 title ms-2">Uptime Kuma</span>
|
<span class="fs-4 title ms-2">Uptime Kuma</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
@ -33,6 +33,14 @@
|
||||||
<Login v-if="! $root.loggedIn && $root.allowLoginDialog" />
|
<Login v-if="! $root.loggedIn && $root.allowLoginDialog" />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
<!-- Mobile Only -->
|
<!-- Mobile Only -->
|
||||||
<div style="width: 100%;height: 60px;" v-if="$root.isMobile"></div>
|
<div style="width: 100%;height: 60px;" v-if="$root.isMobile"></div>
|
||||||
<nav class="bottom-nav" v-if="$root.isMobile">
|
<nav class="bottom-nav" v-if="$root.isMobile">
|
||||||
|
@ -87,7 +95,7 @@ export default {
|
||||||
height: 60px;
|
height: 60px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
background-color: var(--background-secondary);
|
background-color: #fff;
|
||||||
box-shadow: 0 15px 47px 0 rgba(0, 0, 0, 0.05), 0 5px 14px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 15px 47px 0 rgba(0, 0, 0, 0.05), 0 5px 14px 0 rgba(0, 0, 0, 0.05);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -130,6 +138,14 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
color: #AAA;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -9,6 +9,7 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
info: { },
|
||||||
socket: {
|
socket: {
|
||||||
token: null,
|
token: null,
|
||||||
firstConnect: true,
|
firstConnect: true,
|
||||||
|
@ -39,6 +40,10 @@ export default {
|
||||||
transports: ['websocket']
|
transports: ['websocket']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('info', (info) => {
|
||||||
|
this.info = info;
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('setup', (monitorID, data) => {
|
socket.on('setup', (monitorID, data) => {
|
||||||
this.$router.push("/setup")
|
this.$router.push("/setup")
|
||||||
});
|
});
|
||||||
|
@ -275,6 +280,13 @@ export default {
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
||||||
|
// Reload the SPA if the server version is changed.
|
||||||
|
"info.version"(to, from) {
|
||||||
|
if (from && from !== to) {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
remember() {
|
remember() {
|
||||||
localStorage.remember = (this.remember) ? "1" : "0"
|
localStorage.remember = (this.remember) ? "1" : "0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,19 +129,17 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--main-font-color);
|
background-color: $highlight-white;
|
||||||
background-color: var(--background-4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--background-secondary);
|
background-color: #cdf8f4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
min-width: 58px;
|
min-width: 58px;
|
||||||
color: #0a0a0a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-padding {
|
.small-padding {
|
||||||
|
|
|
@ -362,22 +362,29 @@ const aryIannaTimeZones = [
|
||||||
'Pacific/Efate',
|
'Pacific/Efate',
|
||||||
'Pacific/Wallis',
|
'Pacific/Wallis',
|
||||||
'Pacific/Apia',
|
'Pacific/Apia',
|
||||||
'Africa/Johannesburg'
|
'Africa/Johannesburg',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
export function timezoneList() {
|
export function timezoneList() {
|
||||||
|
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
for (let timezone of aryIannaTimeZones) {
|
for (let timezone of aryIannaTimeZones) {
|
||||||
|
|
||||||
let display = dayjs().tz(timezone).format("Z");
|
try {
|
||||||
|
let display = dayjs().tz(timezone).format("Z");
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
name: `(UTC${display}) ${timezone}`,
|
||||||
|
value: timezone,
|
||||||
|
time: getTimezoneOffset(timezone),
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message);
|
||||||
|
console.log("Skip this timezone")
|
||||||
|
}
|
||||||
|
|
||||||
result.push({
|
|
||||||
name: `(UTC${display}) ${timezone}`,
|
|
||||||
value: timezone,
|
|
||||||
time: getTimezoneOffset(timezone),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.sort((a, b) => {
|
result.sort((a, b) => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue