Compare commits

...

51 Commits

Author SHA1 Message Date
Jakub Blažej cd7e76063f
Merge 812b4ad0ff into c86706f189 2024-04-26 14:11:28 -04:00
Louis Lam c86706f189
1.23.13 changes merge to 2.0.0 (#4708) 2024-04-26 15:11:26 +08:00
Louis Lam 1d091739c8
Update server/model/monitor.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-04-26 15:01:03 +08:00
Louis Lam 27bcc968ec
Update server/util-server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-04-25 23:45:30 +08:00
Louis Lam c0db036187
Update server/model/monitor.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2024-04-25 23:45:24 +08:00
Louis Lam 7da5b2311c Fix merge issue 2024-04-25 15:49:32 +08:00
Louis Lam 996db6281b Update dependencies and remove an empty file 2024-04-25 15:46:18 +08:00
Louis Lam 63a380326d Merge branch '1.23.X' into 1.23.13-to-2.0.0
# Conflicts:
#	.github/workflows/auto-test.yml
#	package-lock.json
#	package.json
#	server/database.js
#	server/model/monitor.js
#	server/monitor-types/real-browser-monitor-type.js
#	server/util-server.js
#	test/cypress/unit/i18n.spec.js
2024-04-25 15:42:53 +08:00
Louis Lam 2778929f74 Update to 1.23.13 2024-04-25 15:27:28 +08:00
Louis Lam f71d35e53e Update dependencies 2024-04-25 15:26:49 +08:00
Nelson Chan 1490443618
Fix: Getting TLS certificate through proxy & prometheus update (#4700) 2024-04-24 14:37:17 +08:00
Frank Elsinga add5c128ce
fix: Localisation-matching algorithm missing some edgecase (#4692) 2024-04-21 20:23:34 +08:00
Louis Lam e797abd108 Update to 1.23.12 2024-04-19 01:17:13 +08:00
Louis Lam 7a9e2f5de6
Merge pull request from GHSA-23q2-5gf8-gjpp 2024-04-19 01:08:31 +08:00
Louis Lam 7b5d2a71ff Update dependencies 2024-04-18 20:48:07 +08:00
Nelson Chan 893278bd3d
Feat: Use keylog event to obtain TLS certificate for better reliability [1.23.X] (#4630)
Co-authored-by: Frank Elsinga <frank@elsinga.de>
2024-04-06 18:43:08 +08:00
Adam Stachowicz 0e30ea830d
fix: Update nodemailer to fix GHSA-9h6g-pr28-7cqp [1.23.X] (#4653) 2024-04-05 17:38:24 +02:00
Louis Lam c67a2070b8 Update deps 2024-04-05 12:12:36 +08:00
Adam Stachowicz 9863a10321
fix: Update `axios`, `@actions/github` and `dompurify` [1.23.X] (#4652) 2024-04-05 11:47:46 +08:00
Nelson Chan ee7f8680c1
Fix: Add missing FK for monitor-tls-info table [1.23.X] (#4631) 2024-03-31 12:05:38 +08:00
Nelson Chan c1301804d4
Fix: Fix CI on Windows Runner [1.23.X] (#4633) 2024-03-31 10:33:59 +08:00
Frank Elsinga b385e81608
Improved helptext of how to send mail via the systems mail subsystem (#4477) 2024-03-05 19:40:45 +01:00
Frank Elsinga 812b4ad0ff
Merge branch 'master' into subdirectory 2024-02-19 21:44:24 +01:00
Frank Elsinga f37f55e06c
Fixed lining issues introduced by code reivew 2024-02-11 22:44:57 +01:00
Frank Elsinga 87d7a780e3
changed the helptext a bit to make it more usefull for novice users 2024-02-11 22:40:47 +01:00
apio-sys 0fc372f558 #2793 2024-02-11 20:20:52 +01:00
Joris Le Blansch 67a13e1259 #2793 2024-02-11 20:03:17 +01:00
Nelson Chan 2b8f55194f
Fix: [JSON-Query] Prevent parsing string-only JSON (#4425) 2024-01-28 03:18:24 +08:00
Nelson Chan 288cab6dd7
Fix: Make sure browser is connected before returning (#4417) 2024-01-25 07:59:42 +08:00
AnnAngela b4e45c7ce8
fix(notification-dingding): throw error when failed (#3598) 2024-01-20 03:29:13 +08:00
Frank Elsinga 7635ab54a0
made sure that the i18n does use `navigator.languages` instead of `navigator.language` for automatic language detection (#4244) 2024-01-07 23:55:10 +08:00
Adam Stachowicz 458cdf9f9b
Fix `encodeBase64` for empty password or user in HTTP Basic Authentication (#4326) 2024-01-07 02:06:06 +08:00
Louis Lam 600a34b43c Fix 2023-09-05 13:10:32 +08:00
Louis Lam cb2caaa074 Merge branch 'master' into subdirectory
# Conflicts:
#	index.html
#	server/server.js
#	src/pages/Entry.vue
#	src/pages/NotFound.vue
2023-09-05 12:39:28 +08:00
Jakub Blažej bbf5c19a6e
Update src/mixins/socket.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-09-23 23:10:02 +02:00
Jakub Blažej fcea9af799 Merge branch 'master' of https://github.com/louislam/uptime-kuma into subdirectory 2022-09-23 20:10:45 +02:00
Jakub Blažej b01a534ac9 changed @click to @click.prevent in <a> tags,
replaced <a> with <router-link>,
replaced 'location.href =' navigation by router.push()
2022-07-03 23:40:33 +02:00
Jakub Blažej 86c2ad75ea fix statusPage list href 2022-07-03 21:49:37 +02:00
Jakub Blažej bd112b497a fix linter errors 2022-07-03 21:40:11 +02:00
Jakub Blažej d1caecde98 Merge remote-tracking branch 'upstream/master' into subdirectory 2022-07-03 21:18:29 +02:00
Jakub Blažej 6101eaae27 remove href="#" to prevent unintentional redirections 2022-07-03 20:26:03 +02:00
Jakub Blažej b46aa4014b
Update src/mixins/socket.js: add trailing comma
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2021-12-25 20:10:57 +01:00
Jakub Blažej 20291427e3 remove duplicate slash in push url
remove unnecessary regex escape
2021-12-25 14:13:17 +01:00
Jakub Blažej 92a064d09d fix lint warnings 2021-12-24 13:27:47 +01:00
Jakub Blažej 4f8826cf5c fix manifest.json 401 error when behind basic auth proxy 2021-12-24 12:32:22 +01:00
Jakub Blažej c165f6cad9 fix socket.io path 2021-12-24 12:31:09 +01:00
Jakub Blažej c8d729883c change api call paths to relative
change icon paths to relative
2021-12-24 12:30:29 +01:00
Jakub Blažej 2544363fc0 fix router-links in and to status page 2021-12-24 12:29:18 +01:00
Jakub Blažej 9a405b5773 fix autoGetPrimaryBaseURL 2021-12-24 12:26:49 +01:00
Jakub Blažej 35c428b280 add basic basepath sanitization 2021-12-24 12:26:17 +01:00
Jakub Blažej 8cf827e6e1 add support for deployment in subfolder
configurable using BASE_PATH env variable
2021-12-24 00:48:26 +01:00
34 changed files with 368 additions and 242 deletions

View File

@ -11,6 +11,7 @@ const viteCompressionFilter = /\.(js|mjs|json|css|html|svg)$/i;
// https://vitejs.dev/config/
export default defineConfig({
base: "./",
server: {
port: 3000,
},

View File

@ -0,0 +1,18 @@
BEGIN TRANSACTION;
PRAGMA writable_schema = TRUE;
UPDATE
SQLITE_MASTER
SET
sql = replace(sql,
'monitor_id INTEGER NOT NULL',
'monitor_id INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE'
)
WHERE
name = 'monitor_tls_info'
AND type = 'table';
PRAGMA writable_schema = RESET;
COMMIT;

View File

@ -1,11 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/" >
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png">
<link rel="icon" type="image/svg+xml" href="./icon.svg" />
<link rel="manifest" href="./manifest.json" crossorigin="use-credentials" />
<meta name="theme-color" id="theme-color" content="" />
<meta name="description" content="Uptime Kuma monitoring tool" />
<title>Uptime Kuma</title>
@ -28,6 +29,6 @@
</div>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script type="module" src="./src/main.js"></script>
</body>
</html>

246
package-lock.json generated
View File

@ -14,7 +14,7 @@
"@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "^6.125.0",
"args-parser": "~1.3.0",
"axios": "~0.28.0",
"axios": "~0.28.1",
"axios-ntlm": "1.3.0",
"badge-maker": "~3.3.1",
"bcryptjs": "~2.4.3",
@ -52,7 +52,7 @@
"mongodb": "~4.17.1",
"mqtt": "~4.3.7",
"mssql": "~8.1.4",
"mysql2": "~3.6.2",
"mysql2": "~3.9.6",
"nanoid": "~3.3.4",
"node-cloudflared-tunnel": "~1.0.9",
"node-radius-client": "~1.0.0",
@ -75,14 +75,14 @@
"socket.io": "~4.6.1",
"socket.io-client": "~4.6.1",
"socks-proxy-agent": "6.1.1",
"tar": "~6.1.11",
"tar": "~6.2.1",
"tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2",
"tough-cookie": "~4.1.3",
"ws": "^8.13.0"
},
"devDependencies": {
"@actions/github": "~5.0.1",
"@actions/github": "~5.1.1",
"@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4",
@ -106,7 +106,7 @@
"cross-env": "~7.0.3",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"dompurify": "~3.0.11",
"eslint": "~8.14.0",
"eslint-plugin-jsdoc": "~46.4.6",
"eslint-plugin-vue": "~8.7.1",
@ -127,7 +127,7 @@
"test": "~3.3.0",
"typescript": "~4.4.4",
"v-pagination-3": "~0.1.7",
"vite": "~5.0.10",
"vite": "~5.2.8",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^7.0.15",
"vue": "~3.4.2",
@ -146,7 +146,7 @@
"whatwg-url": "~12.0.1"
},
"engines": {
"node": "14 || 16 || 18 || >= 20.4.0"
"node": "18 || >= 20.4.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@ -159,9 +159,9 @@
}
},
"node_modules/@actions/github": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz",
"integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"dev": true,
"dependencies": {
"@actions/http-client": "^2.0.1",
@ -1962,9 +1962,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"cpu": [
"ppc64"
],
@ -1978,9 +1978,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"cpu": [
"arm"
],
@ -1994,9 +1994,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"cpu": [
"arm64"
],
@ -2010,9 +2010,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"cpu": [
"x64"
],
@ -2026,9 +2026,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"cpu": [
"arm64"
],
@ -2042,9 +2042,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"cpu": [
"x64"
],
@ -2058,9 +2058,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"cpu": [
"arm64"
],
@ -2074,9 +2074,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"cpu": [
"x64"
],
@ -2090,9 +2090,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"cpu": [
"arm"
],
@ -2106,9 +2106,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"cpu": [
"arm64"
],
@ -2122,9 +2122,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"cpu": [
"ia32"
],
@ -2138,9 +2138,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"cpu": [
"loong64"
],
@ -2154,9 +2154,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"cpu": [
"mips64el"
],
@ -2170,9 +2170,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"cpu": [
"ppc64"
],
@ -2186,9 +2186,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"cpu": [
"riscv64"
],
@ -2202,9 +2202,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"cpu": [
"s390x"
],
@ -2218,9 +2218,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"cpu": [
"x64"
],
@ -2234,9 +2234,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"cpu": [
"x64"
],
@ -2250,9 +2250,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"cpu": [
"x64"
],
@ -2266,9 +2266,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"cpu": [
"x64"
],
@ -2282,9 +2282,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"cpu": [
"arm64"
],
@ -2298,9 +2298,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"cpu": [
"ia32"
],
@ -2314,9 +2314,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"cpu": [
"x64"
],
@ -5078,9 +5078,9 @@
"integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="
},
"node_modules/axios": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.28.0.tgz",
"integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.28.1.tgz",
"integrity": "sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
@ -6671,9 +6671,9 @@
}
},
"node_modules/dompurify": {
"version": "2.4.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.9.tgz",
"integrity": "sha512-iHtnxYMotKgOTvxIqq677JsKHvCOkAFqj9x8Mek2zdeHW1XjuFKwjpmZeMaXQRQ8AbJZDbcRz/+r1QhwvFtmQg==",
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.11.tgz",
"integrity": "sha512-Fan4uMuyB26gFV3ovPoEoQbxRRPfTu3CvImyZnhGq5fsIEO+gEFLp45ISFt+kQBWsK5ulDdT0oV28jS1UrwQLg==",
"dev": true
},
"node_modules/domutils": {
@ -7032,9 +7032,9 @@
}
},
"node_modules/esbuild": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"dev": true,
"hasInstallScript": true,
"bin": {
@ -7044,29 +7044,29 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.19.12",
"@esbuild/android-arm": "0.19.12",
"@esbuild/android-arm64": "0.19.12",
"@esbuild/android-x64": "0.19.12",
"@esbuild/darwin-arm64": "0.19.12",
"@esbuild/darwin-x64": "0.19.12",
"@esbuild/freebsd-arm64": "0.19.12",
"@esbuild/freebsd-x64": "0.19.12",
"@esbuild/linux-arm": "0.19.12",
"@esbuild/linux-arm64": "0.19.12",
"@esbuild/linux-ia32": "0.19.12",
"@esbuild/linux-loong64": "0.19.12",
"@esbuild/linux-mips64el": "0.19.12",
"@esbuild/linux-ppc64": "0.19.12",
"@esbuild/linux-riscv64": "0.19.12",
"@esbuild/linux-s390x": "0.19.12",
"@esbuild/linux-x64": "0.19.12",
"@esbuild/netbsd-x64": "0.19.12",
"@esbuild/openbsd-x64": "0.19.12",
"@esbuild/sunos-x64": "0.19.12",
"@esbuild/win32-arm64": "0.19.12",
"@esbuild/win32-ia32": "0.19.12",
"@esbuild/win32-x64": "0.19.12"
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
}
},
"node_modules/escalade": {
@ -10249,9 +10249,9 @@
}
},
"node_modules/mysql2": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz",
"integrity": "sha512-pS/KqIb0xlXmtmqEuTvBXTmLoQ5LmAz5NW/r8UyQ1ldvnprNEj3P9GbmuQQ2J0A4LO+ynotGi6TbscPa8OUb+w==",
"version": "3.9.7",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.7.tgz",
"integrity": "sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw==",
"dependencies": {
"denque": "^2.1.0",
"generate-function": "^2.3.1",
@ -13323,9 +13323,9 @@
"dev": true
},
"node_modules/tar": {
"version": "6.1.15",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz",
"integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@ -13967,14 +13967,14 @@
}
},
"node_modules/vite": {
"version": "5.0.13",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz",
"integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==",
"version": "5.2.10",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz",
"integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==",
"dev": true,
"dependencies": {
"esbuild": "^0.19.3",
"postcss": "^8.4.32",
"rollup": "^4.2.0"
"esbuild": "^0.20.1",
"postcss": "^8.4.38",
"rollup": "^4.13.0"
},
"bin": {
"vite": "bin/vite.js"

View File

@ -23,6 +23,7 @@
"start": "npm run start-server",
"start-server": "node server/server.js",
"start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"start-server-dev:test-subdir": "cross-env NODE_ENV=development BASE_PATH=/my-kuma node server/server.js",
"start-server-dev:watch": "cross-env NODE_ENV=development node --watch server/server.js",
"build": "vite build --config ./config/vite.config.js",
"test": "npm run test-backend && npm run test-e2e",
@ -49,7 +50,7 @@
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
"setup": "git checkout 1.23.11 && npm ci --production && npm run download-dist",
"setup": "git checkout 1.23.13 && npm ci --production && npm run download-dist",
"download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js",
@ -79,7 +80,7 @@
"@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "^6.125.0",
"args-parser": "~1.3.0",
"axios": "~0.28.0",
"axios": "~0.28.1",
"axios-ntlm": "1.3.0",
"badge-maker": "~3.3.1",
"bcryptjs": "~2.4.3",
@ -117,7 +118,7 @@
"mongodb": "~4.17.1",
"mqtt": "~4.3.7",
"mssql": "~8.1.4",
"mysql2": "~3.6.2",
"mysql2": "~3.9.6",
"nanoid": "~3.3.4",
"node-cloudflared-tunnel": "~1.0.9",
"node-radius-client": "~1.0.0",
@ -140,14 +141,14 @@
"socket.io": "~4.6.1",
"socket.io-client": "~4.6.1",
"socks-proxy-agent": "6.1.1",
"tar": "~6.1.11",
"tar": "~6.2.1",
"tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2",
"tough-cookie": "~4.1.3",
"ws": "^8.13.0"
},
"devDependencies": {
"@actions/github": "~5.0.1",
"@actions/github": "~5.1.1",
"@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4",
@ -171,7 +172,7 @@
"cross-env": "~7.0.3",
"delay": "^5.0.0",
"dns2": "~2.0.1",
"dompurify": "~2.4.3",
"dompurify": "~3.0.11",
"eslint": "~8.14.0",
"eslint-plugin-jsdoc": "~46.4.6",
"eslint-plugin-vue": "~8.7.1",
@ -192,7 +193,7 @@
"test": "~3.3.0",
"typescript": "~4.4.4",
"v-pagination-3": "~0.1.7",
"vite": "~5.0.10",
"vite": "~5.2.8",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-vue-devtools": "^7.0.15",
"vue": "~3.4.2",

View File

@ -1,7 +1,7 @@
{
"name": "Uptime Kuma",
"short_name": "Uptime Kuma",
"start_url": "/",
"start_url": "./",
"background_color": "#fff",
"display": "standalone",
"icons": [

View File

@ -245,12 +245,12 @@ class Monitor extends BeanModel {
/**
* Encode user and password to Base64 encoding
* for HTTP "basic" auth, as per RFC-7617
* @param {string} user Username to encode
* @param {string} pass Password to encode
* @returns {string} Encoded username:password
* @param {string|null} user - The username (nullable if not changed by a user)
* @param {string|null} pass - The password (nullable if not changed by a user)
* @returns {string} Encoded Base64 string
*/
encodeBase64(user, pass) {
return Buffer.from(user + ":" + pass).toString("base64");
return Buffer.from(`${user || ""}:${pass || ""}`).toString("base64");
}
/**
@ -533,6 +533,18 @@ class Monitor extends BeanModel {
}
}
let tlsInfo = {};
// Store tlsInfo when secureConnect event is emitted
// The keylog event listener is a workaround to access the tlsSocket
options.httpsAgent.once("keylog", async (line, tlsSocket) => {
tlsSocket.once("secureConnect", async () => {
tlsInfo = checkCertificate(tlsSocket);
tlsInfo.valid = tlsSocket.authorized || false;
await this.handleTlsInfo(tlsInfo);
});
});
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
log.debug("monitor", `[${this.name}] Axios Request`);
@ -542,31 +554,19 @@ class Monitor extends BeanModel {
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;
// Check certificate if https is used
let certInfoStartTime = dayjs().valueOf();
if (this.getUrl()?.protocol === "https:") {
log.debug("monitor", `[${this.name}] Check cert`);
try {
let tlsInfoObject = checkCertificate(res);
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
// fallback for if kelog event is not emitted, but we may still have tlsInfo,
// e.g. if the connection is made through a proxy
if (this.getUrl()?.protocol === "https:" && tlsInfo.valid === undefined) {
const tlsSocket = res.request.res.socket;
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfoObject);
}
if (tlsSocket) {
tlsInfo = checkCertificate(tlsSocket);
tlsInfo.valid = tlsSocket.authorized || false;
} catch (e) {
if (e.message !== "No TLS certificate in response") {
log.error("monitor", "Caught error");
log.error("monitor", e.message);
}
await this.handleTlsInfo(tlsInfo);
}
}
if (process.env.TIMELOGGER === "1") {
log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID === this.id) {
log.info("monitor", res.data);
}
@ -599,8 +599,12 @@ class Monitor extends BeanModel {
let data = res.data;
// convert data to object
if (typeof data === "string") {
data = JSON.parse(data);
if (typeof data === "string" && res.headers["content-type"] !== "application/json") {
try {
data = JSON.parse(data);
} catch (_) {
// Failed to parse as JSON, just process it as a string
}
}
let expression = jsonata(this.jsonPath);
@ -1615,6 +1619,20 @@ class Monitor extends BeanModel {
return oAuthAccessToken;
}
/**
* Store TLS certificate information and check for expiry
* @param {object} tlsInfo Information about the TLS connection
* @returns {Promise<void>}
*/
async handleTlsInfo(tlsInfo) {
await this.updateTlsInfo(tlsInfo);
this.prometheus?.update(null, tlsInfo);
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfo);
}
}
}
module.exports = Monitor;

View File

@ -288,9 +288,9 @@ class StatusPage extends BeanModel {
*/
getIcon() {
if (!this.icon) {
return "/icon.svg";
return "./icon.svg";
} else {
return this.icon;
return "." + this.icon;
}
}

View File

@ -514,7 +514,7 @@ function ApiCache() {
* Return cache performance statistics (hit rate). Suitable for
* putting into a route:
* <code>
* app.get('/api/cache/performance', (req, res) => {
* app.get('./api/cache/performance', (req, res) => {
* res.json(apicache.getPerformance())
* })
* </code>

View File

@ -10,6 +10,10 @@ const jwt = require("jsonwebtoken");
const config = require("../config");
const { RemoteBrowser } = require("../remote-browser");
/**
* Cached instance of a browser
* @type {import ("playwright-core").Browser}
*/
let browser = null;
let allowedList = [];
@ -71,10 +75,12 @@ async function isAllowedChromeExecutable(executablePath) {
/**
* Get the current instance of the browser. If there isn't one, create
* it.
* @returns {Promise<Browser>} The browser
* @returns {Promise<import ("playwright-core").Browser>} The browser
*/
async function getBrowser() {
if (!browser) {
if (browser && browser.isConnected()) {
return browser;
} else {
let executablePath = await Settings.get("chromeExecutable");
executablePath = await prepareChromeExecutable(executablePath);
@ -83,8 +89,9 @@ async function getBrowser() {
//headless: false,
executablePath,
});
return browser;
}
return browser;
}
/**

View File

@ -62,7 +62,7 @@ class DingDing extends NotificationProvider {
if (result.data.errmsg === "ok") {
return true;
}
return false;
throw new Error(result.data.errmsg);
}
/**

View File

@ -80,23 +80,25 @@ class Prometheus {
}
}
try {
monitorStatus.set(this.monitorLabelValues, heartbeat.status);
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
try {
if (typeof heartbeat.ping === "number") {
monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping);
} else {
// Is it good?
monitorResponseTime.set(this.monitorLabelValues, -1);
if (heartbeat) {
try {
monitorStatus.set(this.monitorLabelValues, heartbeat.status);
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
try {
if (typeof heartbeat.ping === "number") {
monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping);
} else {
// Is it good?
monitorResponseTime.set(this.monitorLabelValues, -1);
}
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
}

View File

@ -189,6 +189,8 @@ let needSetup = false;
// Database should be ready now
await server.initAfterDatabaseReady();
server.entryPage = await Settings.get("entryPage");
const mainRouter = express.Router();
await StatusPage.loadDomainMappingList();
log.debug("server", "Adding route");
@ -198,7 +200,7 @@ let needSetup = false;
// ***************************
// Entry Page
app.get("/", async (request, response) => {
mainRouter.get("/", async (request, response) => {
let hostname = request.hostname;
if (await setting("trustProxy")) {
const proxy = request.headers["x-forwarded-host"];
@ -207,7 +209,7 @@ let needSetup = false;
}
}
log.debug("entry", `Request Domain: ${hostname}`);
log.debug("entry", `Request Domain: ${request.hostname}`);
const uptimeKumaEntryPage = server.entryPage;
if (hostname in StatusPage.domainMappingList) {
@ -217,14 +219,14 @@ let needSetup = false;
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
} else if (uptimeKumaEntryPage && uptimeKumaEntryPage.startsWith("statusPage-")) {
response.redirect("/status/" + uptimeKumaEntryPage.replace("statusPage-", ""));
response.redirect(server.basePath + "status/" + uptimeKumaEntryPage.replace("statusPage-", ""));
} else {
response.redirect("/dashboard");
response.redirect(server.basePath + "dashboard");
}
});
app.get("/setup-database-info", (request, response) => {
mainRouter.get("/setup-database-info", (request, response) => {
allowDevAllOrigin(response);
response.json({
runningSetup: false,
@ -233,8 +235,8 @@ let needSetup = false;
});
if (isDev) {
app.use(express.urlencoded({ extended: true }));
app.post("/test-webhook", async (request, response) => {
mainRouter.use(express.urlencoded({ extended: true }));
mainRouter.post("/test-webhook", async (request, response) => {
log.debug("test", request.headers);
log.debug("test", request.body);
response.send("OK");
@ -248,7 +250,7 @@ let needSetup = false;
}
// Robots.txt
app.get("/robots.txt", async (_request, response) => {
mainRouter.get("/robots.txt", async (_request, response) => {
let txt = "User-agent: *\nDisallow:";
if (!await setting("searchEngineIndex")) {
txt += " /";
@ -261,29 +263,29 @@ let needSetup = false;
// Prometheus API metrics /metrics
// With Basic Auth using the first user's username/password
app.get("/metrics", apiAuth, prometheusAPIMetrics());
mainRouter.get("/metrics", apiAuth, prometheusAPIMetrics());
app.use("/", expressStaticGzip("dist", {
mainRouter.use("/", expressStaticGzip("dist", {
enableBrotli: true,
}));
// ./data/upload
app.use("/upload", express.static(Database.uploadDir));
mainRouter.use("/upload", express.static(Database.uploadDir));
app.get("/.well-known/change-password", async (_, response) => {
mainRouter.get("/.well-known/change-password", async (_, response) => {
response.redirect("https://github.com/louislam/uptime-kuma/wiki/Reset-Password-via-CLI");
});
// API Router
const apiRouter = require("./routers/api-router");
app.use(apiRouter);
mainRouter.use(apiRouter);
// Status Page Router
const statusPageRouter = require("./routers/status-page-router");
app.use(statusPageRouter);
mainRouter.use(statusPageRouter);
// Universal Route Handler, must be at the end of all express routes.
app.get("*", async (_request, response) => {
mainRouter.get("*", async (_request, response) => {
if (_request.originalUrl.startsWith("/upload/")) {
response.status(404).send("File not found.");
} else {
@ -291,6 +293,14 @@ let needSetup = false;
}
});
app.use(server.basePath, mainRouter);
if (server.basePath !== "/") {
app.get("/", (request, response) => {
response.status(404).send("Your Uptime Kuma is running at " + server.basePath);
});
}
log.debug("server", "Adding socket handler");
io.on("connection", async (socket) => {
@ -1319,6 +1329,12 @@ let needSetup = false;
await doubleCheckPassword(socket, currentPassword);
}
// Log out all clients if enabling auth
// GHSA-23q2-5gf8-gjpp
if (currentDisabledAuth && !data.disableAuth) {
server.disconnectAllSocketClients(socket.userID, socket.id);
}
const previousChromeExecutable = await Settings.get("chromeExecutable");
const previousNSCDStatus = await Settings.get("nscd");

View File

@ -145,7 +145,7 @@ module.exports.statusPageSocketHandler = (socket) => {
// Convert to file
await ImageDataURI.outputFile(imgDataUrl, Database.uploadDir + filename);
config.logo = `/upload/${filename}?t=` + Date.now();
config.logo = `./upload/${filename}?t=` + Date.now();
} else {
config.icon = imgDataUrl;

View File

@ -84,6 +84,21 @@ class UptimeKumaServer {
// Set default axios timeout to 5 minutes instead of infinity
axios.defaults.timeout = 300 * 1000;
let basePathEnv = process.env.UPTIME_KUMA_BASE_PATH || process.env.BASE_PATH || "/";
if (!basePathEnv.startsWith("/")) {
basePathEnv = "/" + basePathEnv;
}
if (!basePathEnv.endsWith("/")) {
basePathEnv = basePathEnv + "/";
}
if (basePathEnv !== "/") {
log.info("server", "Base Path enabled: " + basePathEnv);
}
this.basePath = basePathEnv;
log.info("server", "Creating express and socket.io instance");
this.app = express();
if (isSSL) {
@ -100,6 +115,7 @@ class UptimeKumaServer {
try {
this.indexHTML = fs.readFileSync("./dist/index.html").toString();
this.indexHTML = this.indexHTML.replace(/<base href.*?>/, `<base href="${this.basePath}">`);
} catch (e) {
// "dist/index.html" is not necessary for development
if (process.env.NODE_ENV !== "development") {
@ -123,6 +139,7 @@ class UptimeKumaServer {
}
this.io = new Server(this.httpServer, {
path: this.basePath + "socket.io"
cors,
allowRequest: async (req, callback) => {
let transport;

View File

@ -653,21 +653,27 @@ const parseCertificateInfo = function (info) {
/**
* Check if certificate is valid
* @param {object} res Response object from axios
* @param {tls.TLSSocket} socket TLSSocket, which may or may not be connected
* @returns {object} Object containing certificate information
* @throws No socket was found to check certificate for
*/
exports.checkCertificate = function (res) {
if (!res.request.res.socket) {
throw new Error("No socket found");
exports.checkCertificate = function (socket) {
let certInfoStartTime = dayjs().valueOf();
// Return null if there is no socket
if (socket === undefined || socket == null) {
return null;
}
const info = res.request.res.socket.getPeerCertificate(true);
const valid = res.request.res.socket.authorized || false;
const info = socket.getPeerCertificate(true);
const valid = socket.authorized || false;
log.debug("cert", "Parsing Certificate Info");
const parsedInfo = parseCertificateInfo(info);
if (process.env.TIMELOGGER === "1") {
log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
}
return {
valid: valid,
certInfo: parsedInfo

View File

@ -6,7 +6,7 @@
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li v-for="(item, key) in chartPeriodOptions" :key="key">
<a class="dropdown-item" :class="{ active: chartPeriodHrs == key }" href="#" @click="chartPeriodHrs = key">{{ item }}</a>
<a class="dropdown-item" :class="{ active: chartPeriodHrs == key }" @click="chartPeriodHrs = key">{{ item }}</a>
</li>
</ul>
</div>
@ -293,6 +293,7 @@ export default {
.dropdown-item {
border-radius: 0.3rem;
padding: 2px 16px 4px;
cursor: pointer;
.dark & {
background: $dark-bg;

View File

@ -5,6 +5,14 @@
<input id="hostname" v-model="$parent.notification.smtpHost" type="text" class="form-control" required>
</div>
<i18n-t tag="div" keypath="Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent" class="form-text">
<template #localhost>
<code>localhost</code>
</template>
<template #local_mta>
<a href="https://wikipedia.org/wiki/Mail_Transfer_Agent" target="_blank">{{ $t("locally configured mail transfer agent") }}</a>
</template>
</i18n-t>
<div class="mb-3">
<label for="port" class="form-label">{{ $t("Port") }}</label>
<input id="port" v-model="$parent.notification.smtpPort" type="number" class="form-control" required min="0" max="65535" step="1">

View File

@ -1,7 +1,7 @@
<template>
<div class="d-flex justify-content-center align-items-center">
<div class="logo d-flex flex-column justify-content-center align-items-center">
<object class="my-4" width="200" height="200" data="/icon.svg" />
<object class="my-4" width="200" height="200" data="./icon.svg" />
<div class="fs-4 fw-bold">Uptime Kuma</div>
<div>{{ $t("Version") }}: {{ $root.info.version }}</div>
<div class="frontend-version">{{ $t("Frontend Version") }}: {{ $root.frontendVersion }}</div>

View File

@ -266,7 +266,8 @@ export default {
* @returns {void}
*/
autoGetPrimaryBaseURL() {
this.settings.primaryBaseURL = location.protocol + "//" + location.host;
const basePath = document.querySelector("head base").getAttribute("href");
this.settings.primaryBaseURL = location.protocol + "//" + location.host + basePath;
},
/**
* Test the chrome executable

View File

@ -11,7 +11,7 @@
<ul class="list-group mb-3" style="border-radius: 1rem;">
<li v-for="(notification, index) in $root.notificationList" :key="index" class="list-group-item">
{{ notification.name }}<br>
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
<a href="#" @click.prevent="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
</li>
</ul>

View File

@ -13,7 +13,7 @@
<li v-for="(proxy, index) in $root.proxyList" :key="index" class="list-group-item">
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("Default") }}</span><br>
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
<a href="#" @click.prevent="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
</li>
</ul>

View File

@ -59,10 +59,29 @@ for (let lang in languageList) {
const rtlLangs = [ "he-IL", "fa", "ar-SY", "ur" ];
export const currentLocale = () => localStorage.locale
|| languageList[navigator.language] && navigator.language
|| languageList[navigator.language.substring(0, 2)] && navigator.language.substring(0, 2)
|| "en";
/**
* Find the best matching locale to display
* If no locale can be matched, the default is "en"
* @returns {string} the locale that should be displayed
*/
export function currentLocale() {
for (const locale of [ localStorage.locale, navigator.language, ...navigator.languages ]) {
// localstorage might not have a value or there might not be a language in `navigator.language`
if (!locale) {
continue;
}
if (locale in messages) {
return locale;
}
// some locales are further specified such as "en-US".
// If we only have a generic locale for this, we can use it too
const genericLocale = locale.split("-")[0];
if (genericLocale in messages) {
return genericLocale;
}
}
return "en";
}
export const localeDirection = () => {
return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr";

View File

@ -63,6 +63,8 @@
"Friendly Name": "Friendly Name",
"URL": "URL",
"Hostname": "Hostname",
"locally configured mail transfer agent": "locally configured mail transfer agent",
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Either enter the hostname of the server you want to connect to or {localhost} if you intend to use a {local_mta}",
"Port": "Port",
"Heartbeat Interval": "Heartbeat Interval",
"Request Timeout": "Request Timeout",

View File

@ -12,7 +12,7 @@
<!-- Desktop header -->
<header v-if="! $root.isMobile" class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom">
<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 class="bi me-2 ms-4" width="40" height="40" data="./icon.svg" />
<span class="fs-4 title">{{ $t("Uptime Kuma") }}</span>
</router-link>
@ -84,7 +84,7 @@
<!-- Mobile header -->
<header v-else class="d-flex flex-wrap justify-content-center pt-2 pb-2 mb-3">
<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 class="bi" width="40" height="40" data="./icon.svg" />
<span class="fs-4 title ms-2">Uptime Kuma</span>
</router-link>
</header>

View File

@ -112,7 +112,12 @@ export default {
url = undefined;
}
socket = io(url);
// always starts and ends with '/'
const basePath = document.querySelector("head base").getAttribute("href");
socket = io(url, {
path: basePath + "socket.io",
});
socket.on("info", (info) => {
this.info = info;

View File

@ -65,7 +65,7 @@ export default {
this.processing = false;
if (res.ok) {
location.href = "/status/" + this.slug + "?edit";
this.$router.push("/status/" + this.slug + "?edit");
} else {
if (res.msg.includes("UNIQUE constraint")) {

View File

@ -587,7 +587,7 @@
<label class="form-check-label" :for=" 'notification' + notification.id">
{{ notification.name }}
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
<a href="#" @click.prevent="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
</label>
<span v-if="notification.isDefault == true" class="badge bg-primary ms-2">{{ $t("Default") }}</span>
@ -614,7 +614,7 @@
<label class="form-check-label" :for="`proxy-${proxy.id}`">
{{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }})
<a href="#" @click="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
<a href="#" @click.prevent="$refs.proxyDialog.show(proxy.id)">{{ $t("Edit") }}</a>
</label>
<span v-if="proxy.default === true" class="badge bg-primary ms-2">{{ $t("default") }}</span>
@ -1049,7 +1049,7 @@ export default {
},
pushURL() {
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=up&msg=OK&ping=";
return this.$root.baseURL + "api/push/" + this.monitor.pushToken + "?status=up&msg=OK&ping=";
},
protoServicePlaceholder() {

View File

@ -25,7 +25,7 @@ export default {
// 3. Vue Frontend Dev (not setup database yet)
let res;
try {
res = (await axios.get("/api/entry-page")).data;
res = (await axios.get("./api/entry-page")).data;
if (res.type === "statusPageMatchedDomain") {
this.statusPageSlug = res.statusPageSlug;

View File

@ -16,7 +16,7 @@
</span>
<!-- use <a> instead of <router-link>, because the heartbeat won't load. -->
<a v-for="statusPage in $root.statusPageList" :key="statusPage.slug" :href="'/status/' + statusPage.slug" class="item">
<a v-for="statusPage in $root.statusPageList" :key="statusPage.slug" :href="'./status/' + statusPage.slug" class="item">
<img :src="icon(statusPage.icon)" alt class="logo me-2" />
<div class="info">
<div class="title">{{ statusPage.title }}</div>

View File

@ -32,7 +32,7 @@
<ul>
<li>{{ $t("Retype the address.") }}</li>
<li><a href="#" class="go-back" @click="goBack()">{{ $t("Go back to the previous page.") }}</a></li>
<li><a href="/" class="go-back">{{ $t("Go back to home page.") }}</a></li>
<li><a href="./" class="go-back">{{ $t("Go back to home page.") }}</a></li>
</ul>
</div>
</div>

View File

@ -3,7 +3,7 @@
<div class="form">
<form @submit.prevent="submit">
<div>
<object width="64" height="64" data="/icon.svg" />
<object width="64" height="64" data="./icon.svg" />
<div style="font-size: 28px; font-weight: bold; margin-top: 5px;">
Uptime Kuma
</div>

View File

@ -154,10 +154,10 @@
{{ $t("Edit Status Page") }}
</button>
<a href="/manage-status-page" class="btn btn-info">
<router-link to="/manage-status-page" class="btn btn-info">
<font-awesome-icon icon="tachometer-alt" />
{{ $t("Go to Dashboard") }}
</a>
</router-link>
</div>
<div v-else>
@ -210,12 +210,12 @@
{{ $t("Style") }}: {{ $t(incident.style) }}
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
<li><a class="dropdown-item" href="#" @click="incident.style = 'info'">{{ $t("info") }}</a></li>
<li><a class="dropdown-item" href="#" @click="incident.style = 'warning'">{{ $t("warning") }}</a></li>
<li><a class="dropdown-item" href="#" @click="incident.style = 'danger'">{{ $t("danger") }}</a></li>
<li><a class="dropdown-item" href="#" @click="incident.style = 'primary'">{{ $t("primary") }}</a></li>
<li><a class="dropdown-item" href="#" @click="incident.style = 'light'">{{ $t("light") }}</a></li>
<li><a class="dropdown-item" href="#" @click="incident.style = 'dark'">{{ $t("dark") }}</a></li>
<li><a class="dropdown-item" href="#" @click.prevent="incident.style = 'info'">{{ $t("info") }}</a></li>
<li><a class="dropdown-item" href="#" @click.prevent="incident.style = 'warning'">{{ $t("warning") }}</a></li>
<li><a class="dropdown-item" href="#" @click.prevent="incident.style = 'danger'">{{ $t("danger") }}</a></li>
<li><a class="dropdown-item" href="#" @click.prevent="incident.style = 'primary'">{{ $t("primary") }}</a></li>
<li><a class="dropdown-item" href="#" @click.prevent="incident.style = 'light'">{{ $t("light") }}</a></li>
<li><a class="dropdown-item" href="#" @click.prevent="incident.style = 'dark'">{{ $t("dark") }}</a></li>
</ul>
</div>
@ -432,7 +432,7 @@ export default {
incident: null,
previousIncident: null,
showImageCropUpload: false,
imgDataUrl: "/icon.svg",
imgDataUrl: "./icon.svg",
loadedTheme: false,
loadedData: false,
baseURL: "",
@ -710,7 +710,7 @@ export default {
this.loading = false;
}).catch( function (error) {
if (error.response.status === 404) {
location.href = "/page-not-found";
this.$router.push("/page-not-found");
}
console.log(error);
});
@ -742,7 +742,7 @@ export default {
data: window.preloadData
}));
} else {
return axios.get("/api/status-page/" + this.slug);
return axios.get("./api/status-page/" + this.slug);
}
},
@ -762,7 +762,7 @@ export default {
updateHeartbeatList() {
// If editMode, it will use the data from websocket.
if (! this.editMode) {
axios.get("/api/status-page/heartbeat/" + this.slug).then((res) => {
axios.get("./api/status-page/heartbeat/" + this.slug).then((res) => {
const { heartbeatList, uptimeList } = res.data;
this.$root.heartbeatList = heartbeatList;
@ -845,7 +845,7 @@ export default {
setTimeout(() => {
this.loading = false;
location.href = "/status/" + this.config.slug;
this.$router.push("/status/" + this.config.slug);
}, time);
} else {
@ -871,7 +871,7 @@ export default {
this.$root.getSocket().emit("deleteStatusPage", this.slug, (res) => {
if (res.ok) {
this.enableEditMode = false;
location.href = "/manage-status-page";
this.$router.push("/manage-status-page");
} else {
this.$root.toastError(res.msg);
}
@ -917,7 +917,7 @@ export default {
* @returns {void}
*/
discard() {
location.href = "/status/" + this.slug;
this.$router.back();
},
/**

3
vue.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
publicPath: "./",
};