Compare commits

...

91 Commits

Author SHA1 Message Date
Louis Lam
398219f847 Update to 1.16.0-beta.0 2022-05-17 01:03:51 +08:00
Louis Lam
7a50f0e3f3 Merge pull request #1589 from AnnAngela/1.15.1_zh-CN
Improve translation work
2022-05-17 00:03:25 +08:00
AnnAngela
4178b003a2 fix value 2022-05-14 15:27:55 +08:00
Louis Lam
8ede6d888f Merge remote-tracking branch 'origin/master' into fix-1448-discord-service-url 2022-05-14 14:37:12 +08:00
Louis Lam
cec0521834 [Discord] Fix ping type should no port, update better naming 2022-05-14 14:36:40 +08:00
Louis Lam
73b603dd10 Merge pull request #1627 from karelkryda/wrong-uptime-for-push
Fixed incorrect uptime calculation for push monitors
2022-05-14 14:18:19 +08:00
Louis Lam
92a43e1f30 Make the sibling beats a bit smaller 2022-05-14 14:11:43 +08:00
Louis Lam
0cf395dfc3 Fix merge issue 2022-05-14 14:06:35 +08:00
Louis Lam
749ca6f4a8 Merge remote-tracking branch 'origin/master' into feature-improve-status-styling
# Conflicts:
#	src/components/HeartbeatBar.vue
#	src/components/PublicGroupList.vue
2022-05-14 14:05:28 +08:00
Louis Lam
66971deaf4 Merge remote-tracking branch 'origin/master' into fix-1448-discord-service-url 2022-05-11 00:51:42 +08:00
Louis Lam
8077744c60 Merge pull request #1604 from c-w/fix-apprise-zulip
Fix apprise integration for Zulip Streams
2022-05-11 00:47:50 +08:00
Jordan Bertasso
c5faf709b8 Merge branch 'master' into fix-1448-discord-service-url 2022-05-10 22:53:55 +10:00
Karel Krýda
7da9f139c1 Bug fix 2022-05-09 21:10:12 +02:00
Karel Krýda
7acbfd2474 eslint fixes too much 2022-05-09 21:05:10 +02:00
Karel Krýda
9f493bbec7 clone master branch 2022-05-09 21:02:29 +02:00
Louis Lam
5bf58cc6c4 Merge pull request #1595 from Saibamen/fix_eslint
Fix ESLint warnings and errors
2022-05-09 13:55:08 +08:00
Louis Lam
d344914ca0 Update docker-compose.yml 2022-05-09 13:47:19 +08:00
Clemens Wolff
b680371746 Make apprise notification title configurable in UI 2022-05-07 11:00:57 -04:00
Louis Lam
e488e2dc0a Merge pull request #1119 from jensneuber/uptime-badges
Add badges
2022-05-07 13:44:20 +08:00
Louis Lam
4e3258579d Merge branch 'master' into uptime-badges
# Conflicts:
#	server/util-server.js
2022-05-07 13:26:47 +08:00
MrEddX
aa8ea6d398 Update bg-BG.js (#1617)
Translation Fixes
2022-05-07 13:01:06 +08:00
Louis Lam
8b0813ceff Fix #1596 hopefully 2022-05-06 14:52:09 +08:00
Louis Lam
91178ce6a5 Merge remote-tracking branch 'origin/master' 2022-05-06 14:41:46 +08:00
Louis Lam
429ad384d0 Fix hardcoded path for error.log and move errorLog() to UptimeKumaServer.errorLog() 2022-05-06 14:41:34 +08:00
Clemens Wolff
4b9dc2890d Convert let to const 2022-05-02 11:16:08 -04:00
Clemens Wolff
f9004bcbed Add optional title to apprise notification 2022-05-02 11:14:26 -04:00
Clemens Wolff
bc174c3325 Extract child process args into variable 2022-05-02 11:00:14 -04:00
Louis Lam
4c2753af46 Remove an unused variable 2022-05-02 13:36:35 +08:00
Louis Lam
c6ba5b621c Remove isPublished, checkPublished which had been removed in upstream. 2022-05-02 13:32:19 +08:00
Louis Lam
96536ae391 Rebuild package-lock.json 2022-05-02 12:36:12 +08:00
Louis Lam
ba46544772 Merge pull request #1603 from Saibamen/fix_task_name
Fix actions/setup-node task name
2022-05-02 12:35:21 +08:00
Adam Stachowicz
5c852db1cf Fix actions/setup-node task name
Addendum to 000cbeb0ce
2022-05-02 01:23:05 +02:00
Adam Stachowicz
069d3765f0 Revert change for StatusPage 2022-05-02 01:13:17 +02:00
Louis Lam
15820c6937 Update SQLite 2022-05-01 19:45:00 +08:00
Louis Lam
000cbeb0ce Lower check-linters node version to 14, add Node.js 18, set timeout 15mins for each job 2022-05-01 18:25:52 +08:00
Louis Lam
e118d59ac8 Merge branch 'master' into uptime-badges 2022-05-01 18:01:26 +08:00
Louis Lam
39aa0a7f07 Merge branch 'fix/chart-error' 2022-05-01 17:57:17 +08:00
Louis Lam
a12dffd1bc Fallback to eqeq for PingChart.vue 2022-05-01 17:56:42 +08:00
Louis Lam
410805052e Log this.chartPeriodHrs 2022-05-01 17:46:43 +08:00
Louis Lam
02a8147f22 Remove undefined variable forceShowContent 2022-05-01 17:31:58 +08:00
Louis Lam
d962ab7a1c Merge branch 'master' into uptime-badges
# Conflicts:
#	package-lock.json
#	server/routers/api-router.js
2022-05-01 17:03:11 +08:00
Louis Lam
63c8d24d6f As legacy-peer-deps is specified in .npmrc, install-legacy and update-legacy are not actually needed. 2022-05-01 12:32:06 +08:00
Louis Lam
254a6bfd36 [CI] Run check linters first 2022-05-01 12:23:28 +08:00
Louis Lam
29f3cbe8c6 Merge pull request #1594 from GOGOsu/patch-1
Fix aliyun-sms "SignatureDoesNotMatch" Error
2022-05-01 12:14:09 +08:00
Louis Lam
53b98ad3e4 Add more comment for aliyun-sms fix 2022-05-01 12:10:47 +08:00
Louis Lam
dbd7c087e0 Merge pull request #1597 from chakflying/fix/chart-error
Fix: Fix chart error on switch back to recent
2022-05-01 11:09:57 +08:00
Nelson Chan
272956025c Fix: Fix chart error on switch back to recent 2022-05-01 05:18:08 +08:00
Louis Lam
db50ba91cc Fix #1593 2022-04-30 21:44:03 +08:00
Louis Lam
42ea3fb412 Update server/util-server.js
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-30 21:36:07 +08:00
Louis Lam
9f8b3151d8 Update server/util-server.js
Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com>
2022-04-30 21:36:00 +08:00
GOGOsu
73e38a13d2 Update server/notification-providers/aliyun-sms.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-30 21:08:35 +08:00
GOGOsu
369477b4b9 Update aliyun-sms.js 2022-04-30 10:45:38 +08:00
GOGOsu
2347a01f7c Update aliyun-sms.js
Add comments for the changed code.
2022-04-30 10:42:59 +08:00
Adam Stachowicz
c114c053d6 Fix ESLint warnings and errors 2022-04-30 03:51:14 +02:00
GOGOsu
ae2c49a729 Update aliyun-sms.js 2022-04-30 06:28:16 +08:00
GOGOsu
b9e72b9645 Update aliyun-sms.js
aliyun-sms.js: escape more characters than encodeURIComponent
see https://help.aliyun.com/document_detail/315526.html
字符A~Z、a~z、0~9以及字符-、_、.、~不编码。对其它ASCII码字符进行编码。
2022-04-30 05:56:10 +08:00
AnnAngela
5a069b278d Update src/components/notifications/PromoSMS.vue
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-29 21:42:54 +08:00
AnnAngela
65ea2e6aeb Update src/components/notifications/PromoSMS.vue
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-29 21:42:48 +08:00
AnnAngela-work
e82fc1df61 Match up en lang file 2022-04-29 20:31:36 +08:00
AnnAngela-work
7dd5f5ea0d Improve translation 2022-04-29 20:26:56 +08:00
AnnAngela-work
45da7c5431 Improve translation work 2022-04-29 20:17:15 +08:00
Jens Neuber
64a33d7455 Update server/util-server.js
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-04-22 07:54:13 +02:00
Jens Neuber
e103ac8335 Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-04-20 10:10:14 +02:00
Jordan Bertasso
288ed1e3ca Merge branch 'master' into fix-1448-discord-service-url 2022-04-15 14:13:44 +10:00
jordanbertasso
0765f05090 Update Discord tests 2022-04-12 09:52:16 +10:00
jordanbertasso
2638d68c97 Cover dns and steam types in Discord notifs 2022-04-12 09:52:07 +10:00
jordanbertasso
1b1e0f6dd9 Add Discord tests 2022-04-10 22:02:30 +10:00
jordanbertasso
0961c6d9b3 Check for ping and port type in discord notifs 2022-04-10 21:45:07 +10:00
jordanbertasso
ce7d8c38c5 [empty commit] pull request for issue #1448 2022-04-10 21:43:52 +10:00
Jens Neuber
454c1687cf Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-02-13 11:12:22 +01:00
Jens Neuber
28be32fc68 Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-01-28 08:36:05 +01:00
Raphael Bernhart
dd3992063e Apply suggestions from code review
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-22 13:59:36 +01:00
Raphael Bernhart
0313acd4c5 🐛 Fix bug where a condition was wrong-false 2022-01-21 17:22:30 +01:00
Raphael Bernhart
cd19b9fc49 💫 Improve hearbeat animation 2022-01-21 17:13:38 +01:00
Raphael Bernhart
c57b2c4d28 💄 Fix spacing to get pixel perfectness 2022-01-21 17:13:24 +01:00
Raphael Bernhart
3dda5938f2 💄 Add condition to div tag for styling reasons 2022-01-21 15:39:49 +01:00
Jens Neuber
f00ec4dfef PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:26:29 +01:00
Jens Neuber
43f8fc701c PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:26:23 +01:00
Jens Neuber
499042504f PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:26:07 +01:00
Jens Neuber
faf6719e7c PR feedback: remove spaces in parenthesis
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:25:56 +01:00
Jens Neuber
a9d264ccfc PR feedback: remove spaces in comments
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
2022-01-05 15:25:42 +01:00
Jens Neuber
df8f93f0c2 clean uptime percentage display 2022-01-05 11:48:25 +01:00
Jens Neuber
28c0e16a0c PR feedback 2022-01-04 16:01:40 +01:00
Jens Neuber
6acc9546a0 PR feedback + remove redundant code + add a test 2022-01-04 16:00:21 +01:00
Jens Neuber
f455e3a454 add shields.io 'style' parameter 2022-01-04 13:43:13 +01:00
Jens Neuber
7abbf421d0 PR feedback 2022-01-04 12:23:16 +01:00
Jens Neuber
3625915a85 add ping, status badge 2022-01-04 12:21:53 +01:00
Jens Neuber
d74404e106 minor fixes 2022-01-03 16:23:23 +01:00
Jens Neuber
1c5bce8afa a little documentation 2022-01-03 16:04:37 +01:00
Jens Neuber
8b5997691e Merge branch 'master' of https://github.com/louislam/uptime-kuma into uptime-badges 2022-01-03 15:49:00 +01:00
Jens Neuber
35360e2069 add badges 2022-01-03 15:48:52 +01:00
42 changed files with 855 additions and 201 deletions

View File

@@ -11,12 +11,14 @@ on:
jobs: jobs:
auto-test: auto-test:
needs: [ check-linters ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy: strategy:
matrix: matrix:
os: [macos-latest, ubuntu-latest, windows-latest] os: [macos-latest, ubuntu-latest, windows-latest]
node: [14, 16, 17] node: [ 14, 16, 17, 18 ]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/ # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps: steps:
@@ -28,7 +30,7 @@ jobs:
with: with:
node-version: ${{ matrix.node }} node-version: ${{ matrix.node }}
cache: 'npm' cache: 'npm'
- run: npm run install-legacy - run: npm install
- run: npm run build - run: npm run build
- run: npm test - run: npm test
env: env:
@@ -41,10 +43,10 @@ jobs:
- run: git config --global core.autocrlf false # Mainly for Windows - run: git config --global core.autocrlf false # Mainly for Windows
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Use Node.js LTS - name: Use Node.js 14
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: 14
cache: 'npm' cache: 'npm'
- run: npm run install-legacy - run: npm install
- run: npm run lint - run: npm run lint

2
CNAME
View File

@@ -1 +1 @@
git.kuma.pet git.kuma.pet

View File

@@ -8,7 +8,7 @@ services:
image: louislam/uptime-kuma:1 image: louislam/uptime-kuma:1
container_name: uptime-kuma container_name: uptime-kuma
volumes: volumes:
- ./uptime-kuma:/app/data - ./uptime-kuma-data:/app/data
ports: ports:
- 3001:3001 - 3001:3001 # <Host Port>:<Container Port>
restart: always restart: always

200
package-lock.json generated
View File

@@ -1,22 +1,23 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.15.0", "version": "1.15.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.15.0", "version": "1.15.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "~1.2.36", "@fortawesome/fontawesome-svg-core": "~1.2.36",
"@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-5", "@fortawesome/vue-fontawesome": "~3.0.0-5",
"@louislam/sqlite3": "~15.0.3", "@louislam/sqlite3": "~15.0.6",
"@popperjs/core": "~2.10.2", "@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.26.1", "axios": "~0.26.1",
"badge-maker": "^3.3.1",
"bcryptjs": "~2.4.3", "bcryptjs": "~2.4.3",
"bootstrap": "5.1.3", "bootstrap": "5.1.3",
"bree": "~7.1.5", "bree": "~7.1.5",
@@ -24,6 +25,7 @@
"chart.js": "~3.6.2", "chart.js": "~3.6.2",
"chartjs-adapter-dayjs": "~1.0.0", "chartjs-adapter-dayjs": "~1.0.0",
"check-password-strength": "^2.0.5", "check-password-strength": "^2.0.5",
"chroma-js": "^2.1.2",
"command-exists": "~1.2.9", "command-exists": "~1.2.9",
"compare-versions": "~3.6.0", "compare-versions": "~3.6.0",
"dayjs": "~1.10.8", "dayjs": "~1.10.8",
@@ -2697,9 +2699,9 @@
} }
}, },
"node_modules/@louislam/sqlite3": { "node_modules/@louislam/sqlite3": {
"version": "15.0.3", "version": "15.0.6",
"resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.3.tgz", "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.6.tgz",
"integrity": "sha512-rCH6PIaa+TgBzpTRnqBKUa4H/5G2hIk5ukYK5rXxK+8hVGykRin3UMGzGejrPzIKzDnZGByIF0XD4ndi6lprRQ==", "integrity": "sha512-+HF/4OEy+yakYzJlSPJbLDtf499t0s0eaglXC9y3Oa9OBZ+dKAaTW5+Ft1RCvfUJLFw/oyYjHtMsg9V+7NT05g==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0", "@mapbox/node-pre-gyp": "^1.0.0",
@@ -3939,6 +3941,14 @@
"url": "https://github.com/sponsors/epoberezkin" "url": "https://github.com/sponsors/epoberezkin"
} }
}, },
"node_modules/anafanafo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz",
"integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==",
"dependencies": {
"char-width-table-consumer": "^1.0.0"
}
},
"node_modules/ansi-align": { "node_modules/ansi-align": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
@@ -4366,6 +4376,22 @@
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
}, },
"node_modules/badge-maker": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz",
"integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==",
"dependencies": {
"anafanafo": "2.0.0",
"css-color-converter": "^2.0.0"
},
"bin": {
"badge": "lib/badge-cli.js"
},
"engines": {
"node": ">= 10",
"npm": ">= 5"
}
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -4443,6 +4469,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/binary-search": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
"integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
},
"node_modules/bintrees": { "node_modules/bintrees": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
@@ -4945,6 +4976,14 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/char-width-table-consumer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz",
"integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
"dependencies": {
"binary-search": "^1.3.5"
}
},
"node_modules/chardet": { "node_modules/chardet": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz",
@@ -5004,6 +5043,29 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/chroma-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz",
"integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==",
"dependencies": {
"cross-env": "^6.0.3"
}
},
"node_modules/chroma-js/node_modules/cross-env": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
"integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
"dependencies": {
"cross-spawn": "^7.0.0"
},
"bin": {
"cross-env": "src/bin/cross-env.js",
"cross-env-shell": "src/bin/cross-env-shell.js"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/ci-info": { "node_modules/ci-info": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz",
@@ -5555,7 +5617,6 @@
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"dependencies": { "dependencies": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
@@ -5574,6 +5635,26 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/css-color-converter": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz",
"integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==",
"dependencies": {
"color-convert": "^0.5.2",
"color-name": "^1.1.4",
"css-unit-converter": "^1.1.2"
}
},
"node_modules/css-color-converter/node_modules/color-convert": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
"integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
},
"node_modules/css-color-converter/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/css-functions-list": { "node_modules/css-functions-list": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz",
@@ -5583,6 +5664,11 @@
"node": ">=12.22" "node": ">=12.22"
} }
}, },
"node_modules/css-unit-converter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
"integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
},
"node_modules/cssesc": { "node_modules/cssesc": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -8694,8 +8780,7 @@
"node_modules/isexe": { "node_modules/isexe": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
"devOptional": true
}, },
"node_modules/isobject": { "node_modules/isobject": {
"version": "3.0.1", "version": "3.0.1",
@@ -13143,7 +13228,6 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -14689,7 +14773,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"dependencies": { "dependencies": {
"shebang-regex": "^3.0.0" "shebang-regex": "^3.0.0"
}, },
@@ -14701,7 +14784,6 @@
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -16532,7 +16614,6 @@
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"devOptional": true,
"dependencies": { "dependencies": {
"isexe": "^2.0.0" "isexe": "^2.0.0"
}, },
@@ -18665,9 +18746,9 @@
} }
}, },
"@louislam/sqlite3": { "@louislam/sqlite3": {
"version": "15.0.3", "version": "15.0.6",
"resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.3.tgz", "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.6.tgz",
"integrity": "sha512-rCH6PIaa+TgBzpTRnqBKUa4H/5G2hIk5ukYK5rXxK+8hVGykRin3UMGzGejrPzIKzDnZGByIF0XD4ndi6lprRQ==", "integrity": "sha512-+HF/4OEy+yakYzJlSPJbLDtf499t0s0eaglXC9y3Oa9OBZ+dKAaTW5+Ft1RCvfUJLFw/oyYjHtMsg9V+7NT05g==",
"requires": { "requires": {
"@mapbox/node-pre-gyp": "^1.0.0", "@mapbox/node-pre-gyp": "^1.0.0",
"node-addon-api": "^4.2.0", "node-addon-api": "^4.2.0",
@@ -19745,6 +19826,14 @@
"uri-js": "^4.2.2" "uri-js": "^4.2.2"
} }
}, },
"anafanafo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz",
"integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==",
"requires": {
"char-width-table-consumer": "^1.0.0"
}
},
"ansi-align": { "ansi-align": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
@@ -20087,6 +20176,15 @@
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
}, },
"badge-maker": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz",
"integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==",
"requires": {
"anafanafo": "2.0.0",
"css-color-converter": "^2.0.0"
}
},
"balanced-match": { "balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -20143,6 +20241,11 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true "dev": true
}, },
"binary-search": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
"integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
},
"bintrees": { "bintrees": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
@@ -20510,6 +20613,14 @@
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
"dev": true "dev": true
}, },
"char-width-table-consumer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz",
"integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==",
"requires": {
"binary-search": "^1.3.5"
}
},
"chardet": { "chardet": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz",
@@ -20551,6 +20662,24 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
}, },
"chroma-js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz",
"integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==",
"requires": {
"cross-env": "^6.0.3"
},
"dependencies": {
"cross-env": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz",
"integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==",
"requires": {
"cross-spawn": "^7.0.0"
}
}
}
},
"ci-info": { "ci-info": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz",
@@ -20993,7 +21122,6 @@
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"requires": { "requires": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
@@ -21006,12 +21134,39 @@
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
"dev": true "dev": true
}, },
"css-color-converter": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz",
"integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==",
"requires": {
"color-convert": "^0.5.2",
"color-name": "^1.1.4",
"css-unit-converter": "^1.1.2"
},
"dependencies": {
"color-convert": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
"integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
}
}
},
"css-functions-list": { "css-functions-list": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz",
"integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==",
"dev": true "dev": true
}, },
"css-unit-converter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
"integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
},
"cssesc": { "cssesc": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -23331,8 +23486,7 @@
"isexe": { "isexe": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
"devOptional": true
}, },
"isobject": { "isobject": {
"version": "3.0.1", "version": "3.0.1",
@@ -26732,8 +26886,7 @@
"path-key": { "path-key": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
"dev": true
}, },
"path-parse": { "path-parse": {
"version": "1.0.7", "version": "1.0.7",
@@ -27893,7 +28046,6 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": { "requires": {
"shebang-regex": "^3.0.0" "shebang-regex": "^3.0.0"
} }
@@ -27901,8 +28053,7 @@
"shebang-regex": { "shebang-regex": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
"dev": true
}, },
"signal-exit": { "signal-exit": {
"version": "3.0.7", "version": "3.0.7",
@@ -29304,7 +29455,6 @@
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"devOptional": true,
"requires": { "requires": {
"isexe": "^2.0.0" "isexe": "^2.0.0"
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "1.15.1", "version": "1.16.0-beta.0",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -10,8 +10,8 @@
"node": "14.* || >=16.*" "node": "14.* || >=16.*"
}, },
"scripts": { "scripts": {
"install-legacy": "npm install --legacy-peer-deps", "install-legacy": "npm install",
"update-legacy": "npm update --legacy-peer-deps", "update-legacy": "npm update",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint-fix:js": "eslint --ext \".js,.vue\" --fix --ignore-path .gitignore .", "lint-fix:js": "eslint --ext \".js,.vue\" --fix --ignore-path .gitignore .",
"lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore", "lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore",
@@ -64,10 +64,11 @@
"@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-regular-svg-icons": "~5.15.4",
"@fortawesome/free-solid-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4",
"@fortawesome/vue-fontawesome": "~3.0.0-5", "@fortawesome/vue-fontawesome": "~3.0.0-5",
"@louislam/sqlite3": "~15.0.3", "@louislam/sqlite3": "~15.0.6",
"@popperjs/core": "~2.10.2", "@popperjs/core": "~2.10.2",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.26.1", "axios": "~0.26.1",
"badge-maker": "^3.3.1",
"bcryptjs": "~2.4.3", "bcryptjs": "~2.4.3",
"bootstrap": "5.1.3", "bootstrap": "5.1.3",
"bree": "~7.1.5", "bree": "~7.1.5",
@@ -75,6 +76,7 @@
"chart.js": "~3.6.2", "chart.js": "~3.6.2",
"chartjs-adapter-dayjs": "~1.0.0", "chartjs-adapter-dayjs": "~1.0.0",
"check-password-strength": "^2.0.5", "check-password-strength": "^2.0.5",
"chroma-js": "^2.1.2",
"command-exists": "~1.2.9", "command-exists": "~1.2.9",
"compare-versions": "~3.6.0", "compare-versions": "~3.6.0",
"dayjs": "~1.10.8", "dayjs": "~1.10.8",

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -1,7 +1,20 @@
const args = require("args-parser")(process.argv); const args = require("args-parser")(process.argv);
const demoMode = args["demo"] || false; const demoMode = args["demo"] || false;
const badgeConstants = {
naColor: "#999",
defaultUpColor: "#66c20a",
defaultDownColor: "#c2290a",
defaultPingColor: "blue", // as defined by badge-maker / shields.io
defaultStyle: "flat",
defaultPingValueSuffix: "ms",
defaultPingLabelSuffix: "h",
defaultUptimeValueSuffix: "%",
defaultUptimeLabelSuffix: "h",
};
module.exports = { module.exports = {
args, args,
demoMode demoMode,
badgeConstants,
}; };

View File

@@ -7,7 +7,7 @@ dayjs.extend(timezone);
const axios = require("axios"); const axios = require("axios");
const { Prometheus } = require("../prometheus"); const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog, mqttAsync } = require("../util-server"); const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mqttAsync } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
const { Notification } = require("../notification"); const { Notification } = require("../notification");
@@ -15,6 +15,7 @@ const { Proxy } = require("../proxy");
const { demoMode } = require("../config"); const { demoMode } = require("../config");
const version = require("../../package.json").version; const version = require("../../package.json").version;
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server");
/** /**
* status: * status:
@@ -181,7 +182,7 @@ class Monitor extends BeanModel {
// undefined if not https // undefined if not https
let tlsInfo = undefined; let tlsInfo = undefined;
if (!previousBeat) { if (!previousBeat || this.type === "push") {
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
this.id, this.id,
]); ]);
@@ -376,9 +377,6 @@ class Monitor extends BeanModel {
log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time); log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time);
if (heartbeatCount <= 0) { if (heartbeatCount <= 0) {
// Fix #922, since previous heartbeat could be inserted by api, it should get from database
previousBeat = await Monitor.getPreviousHeartbeat(this.id);
throw new Error("No heartbeat in the time window"); throw new Error("No heartbeat in the time window");
} else { } else {
// No need to insert successful heartbeat for push type, so end here // No need to insert successful heartbeat for push type, so end here
@@ -521,7 +519,7 @@ class Monitor extends BeanModel {
await beat(); await beat();
} catch (e) { } catch (e) {
console.trace(e); console.trace(e);
errorLog(e, false); UptimeKumaServer.errorLog(e, false);
log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues"); log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues");
if (! this.isStop) { if (! this.isStop) {

View File

@@ -93,8 +93,23 @@ class AliyunSMS extends NotificationProvider {
param2[key] = param[key]; param2[key] = param[key];
} }
// Escape more characters than encodeURIComponent does.
// For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded.
// See https://help.aliyun.com/document_detail/315526.html
// This encoding methods as known as RFC 3986 (https://tools.ietf.org/html/rfc3986)
let moreEscapesTable = function (m) {
return {
"!": "%21",
"*": "%2A",
"'": "%27",
"(": "%28",
")": "%29"
}[m];
};
for (let key in param2) { for (let key in param2) {
data.push(`${encodeURIComponent(key)}=${encodeURIComponent(param2[key])}`); let value = encodeURIComponent(param2[key]).replace(/[!*'()]/g, moreEscapesTable);
data.push(`${encodeURIComponent(key)}=${value}`);
} }
let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`; let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`;

View File

@@ -6,9 +6,14 @@ class Apprise extends NotificationProvider {
name = "apprise"; name = "apprise";
async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
let s = childProcess.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL ]); const args = [ "-vv", "-b", msg, notification.appriseURL ];
if (notification.title) {
args.push("-t");
args.push(notification.title);
}
const s = childProcess.spawnSync("apprise", args);
let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; const output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
if (output) { if (output) {

View File

@@ -22,16 +22,23 @@ class Discord extends NotificationProvider {
return okMsg; return okMsg;
} }
let url; let address;
if (monitorJSON["type"] === "port") { switch (monitorJSON["type"]) {
url = monitorJSON["hostname"]; case "ping":
if (monitorJSON["port"]) { address = monitorJSON["hostname"];
url += ":" + monitorJSON["port"]; break;
} case "port":
case "dns":
} else { case "steam":
url = monitorJSON["url"]; address = monitorJSON["hostname"];
if (monitorJSON["port"]) {
address += ":" + monitorJSON["port"];
}
break;
default:
address = monitorJSON["url"];
break;
} }
// If heartbeatJSON is not null, we go into the normal alerting loop. // If heartbeatJSON is not null, we go into the normal alerting loop.
@@ -48,8 +55,8 @@ class Discord extends NotificationProvider {
value: monitorJSON["name"], value: monitorJSON["name"],
}, },
{ {
name: "Service URL", name: "Service URL / Address",
value: url, value: address,
}, },
{ {
name: "Time (UTC)", name: "Time (UTC)",
@@ -84,7 +91,7 @@ class Discord extends NotificationProvider {
}, },
{ {
name: "Service URL", name: "Service URL",
value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url, value: address.startsWith("http") ? "[Visit Service](" + address + ")" : address,
}, },
{ {
name: "Time (UTC)", name: "Time (UTC)",

View File

@@ -1,5 +1,5 @@
let express = require("express"); let express = require("express");
const { allowDevAllOrigin } = require("../util-server"); const { allowDevAllOrigin, allowAllOrigin, percentageToColor, filterAndJoin } = require("../util-server");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const Monitor = require("../model/monitor"); const Monitor = require("../model/monitor");
@@ -7,6 +7,9 @@ const dayjs = require("dayjs");
const { UP, DOWN, flipStatus, log } = require("../../src/util"); const { UP, DOWN, flipStatus, log } = require("../../src/util");
const StatusPage = require("../model/status_page"); const StatusPage = require("../model/status_page");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const { makeBadge } = require("badge-maker");
const { badgeConstants } = require("../config");
let router = express.Router(); let router = express.Router();
let cache = apicache.middleware; let cache = apicache.middleware;
@@ -197,6 +200,182 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
} }
}); });
router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response) => {
allowAllOrigin(response);
const {
label,
upLabel = "Up",
downLabel = "Down",
upColor = badgeConstants.defaultUpColor,
downColor = badgeConstants.defaultDownColor,
style = badgeConstants.defaultStyle,
value, // for demo purpose only
} = request.query;
try {
const requestedMonitorId = parseInt(request.params.id, 10);
const overrideValue = value !== undefined ? parseInt(value) : undefined;
let publicMonitor = await R.getRow(`
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
WHERE monitor_group.group_id = \`group\`.id
AND monitor_group.monitor_id = ?
AND public = 1
`,
[ requestedMonitorId ]
);
const badgeValues = { style };
if (!publicMonitor) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId);
const state = overrideValue !== undefined ? overrideValue : heartbeat.status === 1;
badgeValues.color = state ? upColor : downColor;
badgeValues.message = label ?? state ? upLabel : downLabel;
}
// build the svg based on given values
const svg = makeBadge(badgeValues);
response.type("image/svg+xml");
response.send(svg);
} catch (error) {
send403(response, error.message);
}
});
router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (request, response) => {
allowAllOrigin(response);
const {
label,
labelPrefix,
labelSuffix = badgeConstants.defaultUptimeLabelSuffix,
prefix,
suffix = badgeConstants.defaultUptimeValueSuffix,
color,
labelColor,
style = badgeConstants.defaultStyle,
value, // for demo purpose only
} = request.query;
try {
const requestedMonitorId = parseInt(request.params.id, 10);
// if no duration is given, set value to 24 (h)
const requestedDuration = request.params.duration !== undefined ? parseInt(request.params.duration, 10) : 24;
const overrideValue = value && parseFloat(value);
let publicMonitor = await R.getRow(`
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
WHERE monitor_group.group_id = \`group\`.id
AND monitor_group.monitor_id = ?
AND public = 1
`,
[ requestedMonitorId ]
);
const badgeValues = { style };
if (!publicMonitor) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const uptime = overrideValue ?? await Monitor.calcUptime(
requestedDuration,
requestedMonitorId
);
// limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits
const cleanUptime = parseFloat(uptime.toPrecision(4));
// use a given, custom color or calculate one based on the uptime value
badgeValues.color = color ?? percentageToColor(uptime);
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a lable string. If a custom label is given, override the default one (requestedDuration)
badgeValues.label = filterAndJoin([ labelPrefix, label ?? requestedDuration, labelSuffix ]);
badgeValues.message = filterAndJoin([ prefix, `${cleanUptime * 100}`, suffix ]);
}
// build the SVG based on given values
const svg = makeBadge(badgeValues);
response.type("image/svg+xml");
response.send(svg);
} catch (error) {
send403(response, error.message);
}
});
router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, response) => {
allowAllOrigin(response);
const {
label,
labelPrefix,
labelSuffix = badgeConstants.defaultPingLabelSuffix,
prefix,
suffix = badgeConstants.defaultPingValueSuffix,
color = badgeConstants.defaultPingColor,
labelColor,
style = badgeConstants.defaultStyle,
value, // for demo purpose only
} = request.query;
try {
const requestedMonitorId = parseInt(request.params.id, 10);
// Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d)
const requestedDuration = Math.min(request.params.duration ? parseInt(request.params.duration, 10) : 24, 720);
const overrideValue = value && parseFloat(value);
const publicAvgPing = parseInt(await R.getCell(`
SELECT AVG(ping) FROM monitor_group, \`group\`, heartbeat
WHERE monitor_group.group_id = \`group\`.id
AND heartbeat.time > DATETIME('now', ? || ' hours')
AND heartbeat.ping IS NOT NULL
AND public = 1
AND heartbeat.monitor_id = ?
`,
[ -requestedDuration, requestedMonitorId ]
));
const badgeValues = { style };
if (!publicAvgPing) {
// return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant
badgeValues.message = "N/A";
badgeValues.color = badgeConstants.naColor;
} else {
const avgPing = parseInt(overrideValue ?? publicAvgPing);
badgeValues.color = color;
// use a given, custom labelColor or use the default badge label color (defined by badge-maker)
badgeValues.labelColor = labelColor ?? "";
// build a lable string. If a custom label is given, override the default one (requestedDuration)
badgeValues.label = filterAndJoin([ labelPrefix, label ?? requestedDuration, labelSuffix ]);
badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]);
}
// build the SVG based on given values
const svg = makeBadge(badgeValues);
response.type("image/svg+xml");
response.send(svg);
} catch (error) {
send403(response, error.message);
}
});
/** /**
* Send a 403 response * Send a 403 response
* @param {Object} res Express response object * @param {Object} res Express response object

View File

@@ -60,7 +60,7 @@ log.info("server", "Importing this project modules");
log.debug("server", "Importing Monitor"); log.debug("server", "Importing Monitor");
const Monitor = require("./model/monitor"); const Monitor = require("./model/monitor");
log.debug("server", "Importing Settings"); log.debug("server", "Importing Settings");
const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog, doubleCheckPassword } = require("./util-server"); const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword } = require("./util-server");
log.debug("server", "Importing Notification"); log.debug("server", "Importing Notification");
const { Notification } = require("./notification"); const { Notification } = require("./notification");
@@ -1694,6 +1694,6 @@ gracefulShutdown(server.httpServer, {
// Catch unexpected errors here // Catch unexpected errors here
process.addListener("unhandledRejection", (error, promise) => { process.addListener("unhandledRejection", (error, promise) => {
console.trace(error); console.trace(error);
errorLog(error, false); UptimeKumaServer.errorLog(error, false);
console.error("If you keep encountering errors, please report to https://github.com/louislam/uptime-kuma/issues"); console.error("If you keep encountering errors, please report to https://github.com/louislam/uptime-kuma/issues");
}); });

View File

@@ -5,13 +5,14 @@ const http = require("http");
const { Server } = require("socket.io"); const { Server } = require("socket.io");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
const { log } = require("../src/util"); const { log } = require("../src/util");
const Database = require("./database");
const util = require("util");
/** /**
* `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue. * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
* @type {UptimeKumaServer} * @type {UptimeKumaServer}
*/ */
class UptimeKumaServer { class UptimeKumaServer {
/** /**
* *
* @type {UptimeKumaServer} * @type {UptimeKumaServer}
@@ -83,6 +84,32 @@ class UptimeKumaServer {
return result; return result;
} }
/**
* Write error to log file
* @param {any} error The error to write
* @param {boolean} outputToConsole Should the error also be output to console?
*/
static errorLog(error, outputToConsole = true) {
const errorLogStream = fs.createWriteStream(Database.dataDir + "/error.log", {
flags: "a"
});
errorLogStream.on("error", () => {
log.info("", "Cannot write to error.log");
});
if (errorLogStream) {
const dateTime = R.isoDateTime();
errorLogStream.write(`[${dateTime}] ` + util.format(error) + "\n");
if (outputToConsole) {
console.error(error);
}
}
errorLogStream.end();
}
} }
module.exports = { module.exports = {

View File

@@ -7,9 +7,9 @@ const { Resolver } = require("dns");
const childProcess = require("child_process"); const childProcess = require("child_process");
const iconv = require("iconv-lite"); const iconv = require("iconv-lite");
const chardet = require("chardet"); const chardet = require("chardet");
const fs = require("fs");
const nodeJsUtil = require("util");
const mqtt = require("mqtt"); const mqtt = require("mqtt");
const chroma = require("chroma-js");
const { badgeConstants } = require("./config");
// From ping-lite // From ping-lite
exports.WIN = /^win/.test(process.platform); exports.WIN = /^win/.test(process.platform);
@@ -206,7 +206,7 @@ exports.dnsResolve = function (hostname, resolverServer, rrtype) {
/** /**
* Retrieve value of setting based on key * Retrieve value of setting based on key
* @param {string} key Key of setting to retrieve * @param {string} key Key of setting to retrieve
* @returns {Promise<Object>} Object representation of setting * @returns {Promise<any>} Value
*/ */
exports.setting = async function (key) { exports.setting = async function (key) {
let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [ let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [
@@ -525,28 +525,32 @@ exports.convertToUTF8 = (body) => {
return str.toString(); return str.toString();
}; };
let logFile; /**
* Returns a color code in hex format based on a given percentage:
try { * 0% => hue = 10 => red
logFile = fs.createWriteStream("./data/error.log", { * 100% => hue = 90 => green
flags: "a" *
}); * @param {number} percentage float, 0 to 1
} catch (_) { } * @param {number} maxHue
* @param {number} minHue, int
* @returns {string}, hex value
*/
exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => {
const hue = percentage * (maxHue - minHue) + minHue;
try {
return chroma(`hsl(${hue}, 90%, 40%)`).hex();
} catch (err) {
return badgeConstants.naColor;
}
};
/** /**
* Write error to log file * Joins and array of string to one string after filtering out empty values
* @param {any} error The error to write *
* @param {boolean} outputToConsole Should the error also be output to console? * @param {string[]} parts
* @param {string} connector
* @returns {string}
*/ */
exports.errorLog = (error, outputToConsole = true) => { exports.filterAndJoin = (parts, connector = "") => {
try { return parts.filter((part) => !!part && part !== "").join(connector);
if (logFile) {
const dateTime = R.isoDateTime();
logFile.write(`[${dateTime}] ` + nodeJsUtil.format(error) + "\n");
if (outputToConsole) {
console.error(error);
}
}
} catch (_) { }
}; };

View File

@@ -367,7 +367,7 @@ textarea.form-control {
.item { .item {
display: block; display: block;
text-decoration: none; text-decoration: none;
padding: 13px 15px 10px 15px; padding: 15px;
border-radius: 10px; border-radius: 10px;
transition: all ease-in-out 0.15s; transition: all ease-in-out 0.15s;

View File

@@ -10,7 +10,10 @@ import { sleep } from "../util.ts";
export default { export default {
props: { props: {
value: [ String, Number ], value: {
type: [ String, Number ],
default: 0,
},
time: { time: {
type: Number, type: Number,
default: 0.3, default: 0.3,

View File

@@ -13,7 +13,10 @@ dayjs.extend(relativeTime);
export default { export default {
props: { props: {
value: String, value: {
type: String,
default: null,
},
dateOnly: { dateOnly: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@@ -1,6 +1,6 @@
<template> <template>
<div ref="wrap" class="wrap" :style="wrapStyle"> <div ref="wrap" class="wrap" :style="wrapStyle">
<div class="hp-bar-big" :style="barStyle"> <div class="hp-bar-big d-flex" :style="barStyle">
<div <div
v-for="(beat, index) in shortBeatList" v-for="(beat, index) in shortBeatList"
:key="index" :key="index"
@@ -8,7 +8,11 @@
:class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }" :class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }"
:style="beatStyle" :style="beatStyle"
:title="getBeatTitle(beat)" :title="getBeatTitle(beat)"
/> @mouseenter="toggleActivateSibling"
@mouseleave="toggleActivateSibling"
>
<div class="beat-inner" />
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -168,6 +172,29 @@ export default {
getBeatTitle(beat) { getBeatTitle(beat) {
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
},
// Toggling the activeSibling class on hover over the current hover item
toggleActivateSibling(e) {
// Variable definition
const element = e.target;
const previous = element.previousSibling;
const next = element.nextSibling;
// Return if the hovered element has empty class
if (element.classList.contains("empty")) {
return;
}
// Check if Previous Sibling is heartbar element and doesn't have the empty class
if (previous.children && !previous.classList.contains("empty")) {
previous.classList.toggle("active-sibling");
}
// Check if Next Sibling is heartbar element and doesn't have the empty class
if (next.children && !next.classList.contains("empty")) {
next.classList.toggle("active-sibling");
}
} }
}, },
}; };
@@ -184,9 +211,10 @@ export default {
.hp-bar-big { .hp-bar-big {
.beat { .beat {
display: inline-block;
background-color: $primary; background-color: $primary;
border-radius: $border-radius; border-radius: $border-radius;
display: inline-block;
transition: all ease 0.6s;
&.empty { &.empty {
background-color: aliceblue; background-color: aliceblue;
@@ -200,11 +228,23 @@ export default {
background-color: $warning; background-color: $warning;
} }
.beat-inner {
border-radius: $border-radius;
display: inline-block;
height: 100%;
width: 5px;
}
&:not(.empty):hover { &:not(.empty):hover {
transition: all ease-in-out 0.15s; transition: all ease 0.15s;
opacity: 0.8; opacity: 0.8;
transform: scale(var(--hover-scale)); transform: scale(var(--hover-scale));
} }
&.active-sibling {
transform: scale(1.15);
transition: all ease 0.15s;
}
} }
} }

View File

@@ -24,7 +24,7 @@ import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
import { LineChart } from "vue-chart-3"; import { LineChart } from "vue-chart-3";
import { useToast } from "vue-toastification"; import { useToast } from "vue-toastification";
import { DOWN } from "../util.ts"; import { DOWN, log } from "../util.ts";
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
@@ -217,8 +217,9 @@ export default {
watch: { watch: {
// Update chart data when the selected chart period changes // Update chart data when the selected chart period changes
chartPeriodHrs: function (newPeriod) { chartPeriodHrs: function (newPeriod) {
if (newPeriod === "0") {
newPeriod = null; // eslint-disable-next-line eqeqeq
if (newPeriod == "0") {
this.heartbeatList = null; this.heartbeatList = null;
this.$root.storage().removeItem(`chart-period-${this.monitorId}`); this.$root.storage().removeItem(`chart-period-${this.monitorId}`);
} else { } else {
@@ -241,7 +242,11 @@ export default {
// And mirror latest change to this.heartbeatList // And mirror latest change to this.heartbeatList
this.$watch(() => this.$root.heartbeatList[this.monitorId], this.$watch(() => this.$root.heartbeatList[this.monitorId],
(heartbeatList) => { (heartbeatList) => {
if (this.chartPeriodHrs !== 0) {
log.debug("ping_chart", `this.chartPeriodHrs type ${typeof this.chartPeriodHrs}, value: ${this.chartPeriodHrs}`);
// eslint-disable-next-line eqeqeq
if (this.chartPeriodHrs != "0") {
const newBeat = heartbeatList.at(-1); const newBeat = heartbeatList.at(-1);
if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) { if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) {
this.heartbeatList.push(heartbeatList.at(-1)); this.heartbeatList.push(heartbeatList.at(-1));

View File

@@ -33,19 +33,19 @@
<template #item="monitor"> <template #item="monitor">
<div class="item"> <div class="item">
<div class="row"> <div class="row">
<div class="col-9 col-md-8 small-padding"> <div class="col-9 col-md-8 small-padding d-flex align-items-center flex-wrap">
<div class="info"> <div class="info d-flex align-items-center gap-3 w-100">
<font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" /> <font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" />
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" /> <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" />
<Uptime :monitor="monitor.element" type="24" :pill="true" /> <Uptime :monitor="monitor.element" type="24" :pill="true" />
{{ monitor.element.name }} {{ monitor.element.name }}
</div> </div>
<div v-if="showTags" class="tags"> <div v-if="showTags && monitor.element.tags.length > 0" class="tags">
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
</div> </div>
</div> </div>
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4"> <div :key="$root.userHeartbeatBar" class="col-3 col-md-4 d-flex align-items-center">
<HeartbeatBar size="small" :monitor-id="monitor.element.id" /> <HeartbeatBar size="small" :monitor-id="monitor.element.id" />
</div> </div>
</div> </div>

View File

@@ -5,7 +5,10 @@
<script> <script>
export default { export default {
props: { props: {
status: Number, status: {
type: Number,
default: 0,
}
}, },
computed: { computed: {

View File

@@ -5,8 +5,14 @@
<script> <script>
export default { export default {
props: { props: {
monitor: Object, monitor: {
type: String, type: Object,
default: null,
},
type: {
type: String,
default: null,
},
pill: { pill: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@@ -8,6 +8,9 @@
<a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a> <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a>
</i18n-t> </i18n-t>
</div> </div>
<label for="title" class="form-label">{{ $t("Title") }}</label>
<input id="title" v-model="$parent.notification.title" type="text" class="form-control">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<i18n-t tag="p" keypath="Status:"> <i18n-t tag="p" keypath="Status:">

View File

@@ -1,12 +1,11 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="clicksendsms-login" class="form-label">API Username</label> <label for="clicksendsms-login" class="form-label">{{ $t("API Username") }}</label>
<div class="form-text"> <i18n-t tag="div" class="form-text" keypath="wayToGetClickSendSMSToken">
{{ $t("apiCredentials") }}
<a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a> <a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a>
</div> </i18n-t>
<input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required> <input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required>
<label for="clicksendsms-key" class="form-label">API Key</label> <label for="clicksendsms-key" class="form-label">{{ $t("API Key") }}</label>
<HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
<div class="mb-3"> <div class="mb-3">
@@ -16,15 +15,15 @@
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="clicksendsms-to-number" class="form-label">Recipient Number</label> <label for="clicksendsms-to-number" class="form-label">{{ $t("Recipient Number") }}</label>
<input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required> <input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="clicksendsms-sender-name" class="form-label">From Name/Number - <label for="clicksendsms-sender-name" class="form-label">{{ $t("From Name/Number") }} -
<a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">More Info</a> <a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">{{ $t("Read more") }}</a>
</label> </label>
<input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control"> <input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control">
<div class="form-text">Leave blank to use a shared sender number.</div> <div class="form-text">{{ $t("Leave blank to use a shared sender number.") }}</div>
</div> </div>
</template> </template>
<script> <script>

View File

@@ -7,7 +7,7 @@
<b>{{ $t("Basic Settings") }}</b> <b>{{ $t("Basic Settings") }}</b>
</i18n-t> </i18n-t>
<div class="mb-3" style="margin-top: 12px;"> <div class="mb-3" style="margin-top: 12px;">
<label for="line-user-id" class="form-label">User ID</label> <label for="line-user-id" class="form-label">{{ $t("User ID") }}</label>
<input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required> <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required>
</div> </div>
<i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text"> <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text">

View File

@@ -1,18 +1,18 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="octopush-version" class="form-label">Octopush API Version</label> <label for="octopush-version" class="form-label">{{ $t("Octopush API Version") }}</label>
<select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select"> <select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select">
<option value="2">Octopush (endpoint: api.octopush.com)</option> <option value="2">{{ $t("octopush") }} ({{ $t("endpoint") }}: api.octopush.com)</option>
<option value="1">Legacy Octopush-DM (endpoint: www.octopush-dm.com)</option> <option value="1">{{ $t("Legacy Octopush-DM") }} ({{ $t("endpoint") }}: www.octopush-dm.com)</option>
</select> </select>
<div class="form-text"> <div class="form-text">
{{ $t("octopushLegacyHint") }} {{ $t("octopushLegacyHint") }}
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="octopush-key" class="form-label">API KEY</label> <label for="octopush-key" class="form-label">{{ $t("octopushAPIKey") }}</label>
<HiddenInput id="octopush-key" v-model="$parent.notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="octopush-key" v-model="$parent.notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
<label for="octopush-login" class="form-label">API LOGIN</label> <label for="octopush-login" class="form-label">{{ $t("octopushLogin") }}</label>
<input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required> <input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">

View File

@@ -1,8 +1,8 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="promosms-login" class="form-label">API LOGIN</label> <label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label>
<input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required> <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required>
<label for="promosms-key" class="form-label">API PASSWORD</label> <label for="promosms-key" class="form-label">{{ $("promosmsPassword") }}</label>
<HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
<div class="mb-3"> <div class="mb-3">

View File

@@ -18,28 +18,29 @@
</select> </select>
<label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label> <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label>
<select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select"> <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select">
<option>pushover</option> <option value="pushover">{{ $t("pushoversounds pushover") }}</option>
<option>bike</option> <option value="bike">{{ $t("pushoversounds bike") }}</option>
<option>bugle</option> <option value="bugle">{{ $t("pushoversounds bugle") }}</option>
<option>cashregister</option> <option value="cashregister">{{ $t("pushoversounds cashregister") }}</option>
<option>classical</option> <option value="classical">{{ $t("pushoversounds classical") }}</option>
<option>cosmic</option> <option value="cosmic">{{ $t("pushoversounds cosmic") }}</option>
<option>falling</option> <option value="falling">{{ $t("pushoversounds falling") }}</option>
<option>gamelan</option> <option value="gamelan">{{ $t("pushoversounds gamelan") }}</option>
<option>incoming</option> <option value="incoming">{{ $t("pushoversounds incoming") }}</option>
<option>intermission</option> <option value="intermission">{{ $t("pushoversounds intermission") }}</option>
<option>mechanical</option> <option value="magic">{{ $t("pushoversounds magic") }}</option>
<option>pianobar</option> <option value="mechanical">{{ $t("pushoversounds mechanical") }}</option>
<option>siren</option> <option value="pianobar">{{ $t("pushoversounds pianobar") }}</option>
<option>spacealarm</option> <option value="siren">{{ $t("pushoversounds siren") }}</option>
<option>tugboat</option> <option value="spacealarm">{{ $t("pushoversounds spacealarm") }}</option>
<option>alien</option> <option value="tugboat">{{ $t("pushoversounds tugboat") }}</option>
<option>climb</option> <option value="alien">{{ $t("pushoversounds alien") }}</option>
<option>persistent</option> <option value="climb">{{ $t("pushoversounds climb") }}</option>
<option>echo</option> <option value="persistent">{{ $t("pushoversounds persistent") }}</option>
<option>updown</option> <option value="echo">{{ $t("pushoversounds echo") }}</option>
<option>vibrate</option> <option value="updown">{{ $t("pushoversounds updown") }}</option>
<option>none</option> <option value="vibrate">{{ $t("pushoversounds vibrate") }}</option>
<option value="none">{{ $t("pushoversounds none") }}</option>
</select> </select>
<div class="form-text"> <div class="form-text">
<span style="color: red;"><sup>*</sup></span>{{ $t("Required") }} <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }}

View File

@@ -1,11 +1,11 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="pushy-app-token" class="form-label">API_KEY</label> <label for="pushy-app-token" class="form-label">{{ $t("pushyAPIKey") }}</label>
<HiddenInput id="pushy-app-token" v-model="$parent.notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="pushy-app-token" v-model="$parent.notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="pushy-user-key" class="form-label">USER_TOKEN</label> <label for="pushy-user-key" class="form-label">{{ $t("pushyToken") }}</label>
<div class="input-group mb-3"> <div class="input-group mb-3">
<HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="mb-3"> <div class="mb-3">
<label for="push-api-key" class="form-label">API_KEY</label> <label for="push-api-key" class="form-label">{{ $t("API Key") }}</label>
<HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput> <HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput>
</div> </div>

View File

@@ -9,11 +9,11 @@
<div class="mt-1"> <div class="mt-1">
<div class="form-check"> <div class="form-check">
<label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> Show update if available</label> <label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> {{ $t("Show update if available") }}</label>
</div> </div>
<div class="form-check"> <div class="form-check">
<label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> Also check beta release</label> <label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> {{ $t("Also check beta release") }}</label>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -12,15 +12,15 @@ export default {
keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра", keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра",
pauseDashboardHome: "Пауза", pauseDashboardHome: "Пауза",
deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?", deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?",
deleteNotificationMsg: "Наистина ли желаете да изтриете това известяване за всички монитори?", deleteNotificationMsg: "Наистина ли желаете да изтриете това известие за всички монитори?",
resolverserverDescription: "Cloudflare е сървърът по подразбиране, но можете да го промените по всяко време.", resolverserverDescription: "Cloudflare е сървърът по подразбиране, но можете да го промените по всяко време.",
rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате", rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате",
pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?", pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?",
enableDefaultNotificationDescription: "За всеки нов монитор това известяване ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.", enableDefaultNotificationDescription: "За всеки нов монитор това известие ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.",
clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?", clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?",
clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?", clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?",
confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?", confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?",
importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известяване със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известяване.", importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известие със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известие.",
confirmImportMsg: "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.", confirmImportMsg: "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.",
twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи", twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи",
tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.", tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.",
@@ -76,9 +76,9 @@ export default {
"Max. Redirects": "Макс. брой пренасочвания", "Max. Redirects": "Макс. брой пренасочвания",
"Accepted Status Codes": "Допустими статус кодове", "Accepted Status Codes": "Допустими статус кодове",
Save: "Запази", Save: "Запази",
Notifications: "Известявания", Notifications: "Известия",
"Not available, please setup.": "Не са налични. Моля, настройте.", "Not available, please setup.": "Не са налични. Моля, настройте.",
"Setup Notification": "Настрой известяване", "Setup Notification": "Настрой известие",
Light: "Светла", Light: "Светла",
Dark: "Тъмна", Dark: "Тъмна",
Auto: "Автоматично", Auto: "Автоматично",
@@ -109,7 +109,7 @@ export default {
Login: "Вход", Login: "Вход",
"No Monitors, please": "Все още няма монитори. Моля, добавете поне ", "No Monitors, please": "Все още няма монитори. Моля, добавете поне ",
"add one": "един.", "add one": "един.",
"Notification Type": "Тип известяване", "Notification Type": "Тип известие",
Email: "Имейл", Email: "Имейл",
Test: "Тест", Test: "Тест",
"Certificate Info": "Информация за сертификат", "Certificate Info": "Информация за сертификат",
@@ -131,9 +131,9 @@ export default {
Events: "Събития", Events: "Събития",
Heartbeats: "Проверки", Heartbeats: "Проверки",
"Auto Get": "Авт. попълване", "Auto Get": "Авт. попълване",
backupDescription: "Можете да архивирате всички монитори и всички известявания в JSON файл.", backupDescription: "Можете да архивирате всички монитори и всички известия в JSON файл.",
backupDescription2: "PS: Имайте предвид, че данните за история и събития няма да бъдат включени.", backupDescription2: "PS: Имайте предвид, че данните за история и събития няма да бъдат включени.",
backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", backupDescription3: "Чувствителни данни, като токен кодове за известия, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.",
alertNoFile: "Моля, изберете файл за импортиране.", alertNoFile: "Моля, изберете файл за импортиране.",
alertWrongFileType: "Моля, изберете JSON файл.", alertWrongFileType: "Моля, изберете JSON файл.",
"Clear all statistics": "Изтрий цялата статистика", "Clear all statistics": "Изтрий цялата статистика",
@@ -202,7 +202,7 @@ export default {
"Push URL": "Генериран Push URL адрес", "Push URL": "Генериран Push URL адрес",
needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди", needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди",
pushOptionalParams: "Допълнителни, но не задължителни параметри: {0}", pushOptionalParams: "Допълнителни, но не задължителни параметри: {0}",
defaultNotificationName: "Моето {notification} известяване ({number})", defaultNotificationName: "Моето {notification} известие ({number})",
here: "тук", here: "тук",
Required: "Задължително поле", Required: "Задължително поле",
"Bot Token": "Бот токен", "Bot Token": "Бот токен",
@@ -252,7 +252,7 @@ export default {
"Notification Sound": "Звуков сигнал", "Notification Sound": "Звуков сигнал",
"More info on:": "Повече информация на: {0}", "More info on:": "Повече информация на: {0}",
pushoverDesc1: "Приоритет Спешно (2) по подразбиране изчаква 30 секунди между повторните опити и изтича след 1 час.", pushoverDesc1: "Приоритет Спешно (2) по подразбиране изчаква 30 секунди между повторните опити и изтича след 1 час.",
pushoverDesc2: "Ако желаете да изпратите известявания до различни устройства, попълнете полето Устройство.", pushoverDesc2: "Ако желаете да изпратите известия до различни устройства, попълнете полето Устройство.",
"SMS Type": "SMS тип", "SMS Type": "SMS тип",
octopushTypePremium: "Премиум (Бърз - препоръчителен в случай на тревога)", octopushTypePremium: "Премиум (Бърз - препоръчителен в случай на тревога)",
octopushTypeLowCost: "Евтин (Бавен - понякога бива блокиран от оператора)", octopushTypeLowCost: "Евтин (Бавен - понякога бива блокиран от оператора)",
@@ -275,7 +275,7 @@ export default {
lineDevConsoleTo: "Line - Конзола за разработчици - {0}", lineDevConsoleTo: "Line - Конзола за разработчици - {0}",
"Basic Settings": "Основни настройки", "Basic Settings": "Основни настройки",
"User ID": "Потребител ID", "User ID": "Потребител ID",
"Messaging API": "API за известяване", "Messaging API": "API за съобщаване",
wayToGetLineChannelToken: "Необходимо е първо да посетите {0}, за да създадете (Messaging API) за доставчик и канал, след което може да вземете токен кода за канал и потребителско ID от споменатите по-горе елементи на менюто.", wayToGetLineChannelToken: "Необходимо е първо да посетите {0}, за да създадете (Messaging API) за доставчик и канал, след което може да вземете токен кода за канал и потребителско ID от споменатите по-горе елементи на менюто.",
"Icon URL": "URL адрес за иконка", "Icon URL": "URL адрес за иконка",
aboutIconURL: "Може да предоставите линк към картинка в поле \"URL Адрес за иконка\" за да отмените картинката на профила по подразбиране. Няма да се използва, ако вече сте настроили емотикон.", aboutIconURL: "Може да предоставите линк към картинка в поле \"URL Адрес за иконка\" за да отмените картинката на профила по подразбиране. Няма да се използва, ако вече сте настроили емотикон.",
@@ -291,7 +291,7 @@ export default {
matrixHomeserverURL: "Сървър URL адрес (започва с http(s):// и порт по желание)", matrixHomeserverURL: "Сървър URL адрес (започва с http(s):// и порт по желание)",
"Internal Room Id": "ID на вътрешна стая", "Internal Room Id": "ID на вътрешна стая",
matrixDesc1: "Може да намерите \"ID на вътрешна стая\" в разширените настройки на стаята във вашия Matrix клиент. Примерен изглед: !QMdRCpUIfLwsfjxye6:home.server.", matrixDesc1: "Може да намерите \"ID на вътрешна стая\" в разширените настройки на стаята във вашия Matrix клиент. Примерен изглед: !QMdRCpUIfLwsfjxye6:home.server.",
matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известяванията. Токен код за достъп ще получите изпълнявайки {0}", matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известията. Токен код за достъп ще получите изпълнявайки {0}",
Method: "Метод", Method: "Метод",
Body: "Съобщение", Body: "Съобщение",
Headers: "Хедъри", Headers: "Хедъри",
@@ -449,7 +449,7 @@ export default {
Customize: "Персонализирай", Customize: "Персонализирай",
"Custom Footer": "Персонализиран долен колонтитул", "Custom Footer": "Персонализиран долен колонтитул",
"Custom CSS": "Потребителски CSS", "Custom CSS": "Потребителски CSS",
"Domain Name Expiry Notification": "Известяване при изтичащ домейн", "Domain Name Expiry Notification": "Известие при изтичащ домейн",
Proxy: "Прокси", Proxy: "Прокси",
"Date Created": "Дата на създаване", "Date Created": "Дата на създаване",
onebotHttpAddress: "OneBot HTTP адрес", onebotHttpAddress: "OneBot HTTP адрес",

View File

@@ -464,4 +464,55 @@ export default {
"Domain Names": "Domain Names", "Domain Names": "Domain Names",
signedInDisp: "Signed in as {0}", signedInDisp: "Signed in as {0}",
signedInDispDisabled: "Auth Disabled.", signedInDispDisabled: "Auth Disabled.",
"Certificate Expiry Notification": "Certificate Expiry Notification",
"API Username": "API Username",
"API Key": "API Key",
"Recipient Number": "Recipient Number",
"From Name/Number": "From Name/Number",
"Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.",
"Octopush API Version": "Octopush API Version",
"Legacy Octopush-DM": "Legacy Octopush-DM",
"endpoint": "endpoint",
octopushAPIKey: "\"API key\" from HTTP API credentials in control panel",
octopushLogin: "\"Login\" from HTTP API credentials in control panel",
promosmsLogin: "API Login Name",
promosmsPassword: "API Password",
"pushoversounds pushover": "Pushover (default)",
"pushoversounds bike": "Bike",
"pushoversounds bugle": "Bugle",
"pushoversounds cashregister": "Cash Register",
"pushoversounds classical": "Classical",
"pushoversounds cosmic": "Cosmic",
"pushoversounds falling": "Falling",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Incoming",
"pushoversounds intermission": "Intermission",
"pushoversounds magic": "Magic",
"pushoversounds mechanical": "Mechanical",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Siren",
"pushoversounds spacealarm": "Space Alarm",
"pushoversounds tugboat": "Tug Boat",
"pushoversounds alien": "Alien Alarm (long)",
"pushoversounds climb": "Climb (long)",
"pushoversounds persistent": "Persistent (long)",
"pushoversounds echo": "Pushover Echo (long)",
"pushoversounds updown": "Up Down (long)",
"pushoversounds vibrate": "Vibrate Only",
"pushoversounds none": "None (silent)",
pushyAPIKey: "Secret API Key",
pushyToken: "Device token",
"Show update if available": "Show update if available",
"Also check beta release": "Also check beta release",
"Using a Reverse Proxy?": "Using a Reverse Proxy?",
"Check how to config it for WebSocket": "Check how to config it for WebSocket",
"Steam Game Server": "Steam Game Server",
"Most likely causes:": "Most likely causes:",
"The resource is no longer available.": "The resource is no longer available.",
"There might be a typing error in the address.": "There might be a typing error in the address.",
"What you can try:": "What you can try:",
"Retype the address.": "Retype the address.",
"Go back to the previous page.": "Go back to the previous page.",
"Coming Soon": "Coming Soon",
wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .",
}; };

View File

@@ -88,7 +88,7 @@ export default {
Dark: "黑暗", Dark: "黑暗",
Auto: "自动", Auto: "自动",
"Theme - Heartbeat Bar": "主题 - 心跳栏", "Theme - Heartbeat Bar": "主题 - 心跳栏",
Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示” Normal: "正常",
Bottom: "靠下", Bottom: "靠下",
None: "不显示", None: "不显示",
Timezone: "时区", Timezone: "时区",
@@ -398,11 +398,9 @@ export default {
Invalid: "无效", Invalid: "无效",
AccessKeyId: "AccessKey ID", AccessKeyId: "AccessKey ID",
SecretAccessKey: "AccessKey Secret", SecretAccessKey: "AccessKey Secret",
/* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */
PhoneNumbers: "PhoneNumbers", PhoneNumbers: "PhoneNumbers",
TemplateCode: "TemplateCode", TemplateCode: "TemplateCode",
SignName: "SignName", SignName: "SignName",
/* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */
"Bark Endpoint": "Bark 接入点", "Bark Endpoint": "Bark 接入点",
"Device Token": "Apple Device Token", "Device Token": "Apple Device Token",
Platform: "平台", Platform: "平台",
@@ -441,7 +439,7 @@ export default {
"No Proxy": "无代理", "No Proxy": "无代理",
"HTTP Basic Auth": "HTTP 基础身份验证", "HTTP Basic Auth": "HTTP 基础身份验证",
"New Status Page": "新的状态页", "New Status Page": "新的状态页",
"Page Not Found": "状态页未找到", "Page Not Found": "未找到该页面",
"Reverse Proxy": "反向代理", "Reverse Proxy": "反向代理",
"Subject:": "颁发给:", "Subject:": "颁发给:",
"Valid To:": "有效期至:", "Valid To:": "有效期至:",
@@ -469,4 +467,57 @@ export default {
"Footer Text": "底部自定义文本", "Footer Text": "底部自定义文本",
"Show Powered By": "显示 Powered By", "Show Powered By": "显示 Powered By",
"Domain Names": "域名", "Domain Names": "域名",
"Certificate Expiry Notification": "证书到期时通知",
"API Username": "API 凭证 Username",
"API Key": "API 凭证 Key",
"Recipient Number": "收件人手机号码",
"From Name/Number": "发件人名称/手机号码",
"Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码",
"Octopush API Version": "Octopush API 版本",
"Legacy Octopush-DM": "旧版本 Octopush-DM",
endpoint: "接入点",
octopushAPIKey: "控制台 HTTP API credentials 里的 \"API key\"",
octopushLogin: "控制台 HTTP API credentials 里的 \"Login\"",
promosmsLogin: "API 登录名",
promosmsPassword: "API 密码",
"pushoversounds pushover": "Pushover默认",
"pushoversounds bike": "Bike",
"pushoversounds bugle": "Bugle",
"pushoversounds cashregister": "Cash Register",
"pushoversounds classical": "Classical",
"pushoversounds cosmic": "Cosmic",
"pushoversounds falling": "Falling",
"pushoversounds gamelan": "Gamelan",
"pushoversounds incoming": "Incoming",
"pushoversounds intermission": "Intermission",
"pushoversounds magic": "Magic",
"pushoversounds mechanical": "Mechanical",
"pushoversounds pianobar": "Piano Bar",
"pushoversounds siren": "Siren",
"pushoversounds spacealarm": "Space Alarm",
"pushoversounds tugboat": "Tug Boat",
"pushoversounds alien": "Alien Alarm长铃声",
"pushoversounds climb": "Climb长铃声",
"pushoversounds persistent": "Persistent长铃声",
"pushoversounds echo": "Pushover Echo长铃声",
"pushoversounds updown": "Up Down长铃声",
"pushoversounds vibrate": "仅震动",
"pushoversounds none": "无(禁音)",
pushyAPIKey: "API 密钥",
pushyToken: "设备 Token",
"Show update if available": "有更新时通知",
"Also check beta release": "一并检查 Beta 版更新",
"Using a Reverse Proxy?": "正在使用反向代理?",
"Check how to config it for WebSocket": "查看如何将反向代理与 WebSocket 一起使用",
"Steam Game Server": "Steam 游戏服务器",
"Most likely causes:": "最可能的原因:",
"The resource is no longer available.": "您所请求的资源已不再可用;",
"There might be a typing error in the address.": "您输入的地址可能有误。",
"What you can try:": "您可以尝试以下操作:",
"Retype the address.": "重新输入地址;",
"Go back to the previous page.": "返回到上一页面。",
"Coming Soon": "即将推出",
wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。",
signedInDisp: "当前用户: {0}",
signedInDispDisabled: "已禁用身份验证",
}; };

View File

@@ -4,7 +4,7 @@
<div class="container-fluid"> <div class="container-fluid">
{{ $root.connectionErrorMsg }} {{ $root.connectionErrorMsg }}
<div v-if="$root.showReverseProxyGuide"> <div v-if="$root.showReverseProxyGuide">
Using a Reverse Proxy? <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">Check how to config it for WebSocket</a> {{ $t("Using a Reverse Proxy?") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">{{ $t("Check how to config it for WebSocket") }}</a>
</div> </div>
</div> </div>
</div> </div>
@@ -33,7 +33,7 @@
</li> </li>
<li v-if="$root.loggedIn" class="nav-item"> <li v-if="$root.loggedIn" class="nav-item">
<div class="dropdown dropdown-profile-pic"> <div class="dropdown dropdown-profile-pic">
<div type="button" class="nav-link" data-bs-toggle="dropdown"> <div class="nav-link" data-bs-toggle="dropdown">
<div class="profile-pic">{{ $root.usernameFirstChar }}</div> <div class="profile-pic">{{ $root.usernameFirstChar }}</div>
<font-awesome-icon icon="angle-down" /> <font-awesome-icon icon="angle-down" />
</div> </div>
@@ -71,7 +71,7 @@
</header> </header>
<main> <main>
<router-view v-if="$root.loggedIn || forceShowContent" /> <router-view v-if="$root.loggedIn" />
<Login v-if="! $root.loggedIn && $root.allowLoginDialog" /> <Login v-if="! $root.loggedIn && $root.allowLoginDialog" />
</main> </main>

View File

@@ -30,7 +30,7 @@
Push Push
</option> </option>
<option value="steam"> <option value="steam">
Steam Game Server {{ $t("Steam Game Server") }}
</option> </option>
<option value="mqtt"> <option value="mqtt">
MQTT MQTT
@@ -421,7 +421,7 @@ export default {
}, },
pushURL() { pushURL() {
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=true&msg=OK&ping="; return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=up&msg=OK&ping=";
}, },
bodyPlaceholder() { bodyPlaceholder() {

View File

@@ -22,16 +22,16 @@
</div> </div>
<div class="guide"> <div class="guide">
Most likely causes: {{ $t("Most likely causes:") }}
<ul> <ul>
<li>The resource is no longer available.</li> <li>{{ $t("The resource is no longer available.") }}</li>
<li>There might be a typing error in the address.</li> <li>{{ $t("There might be a typing error in the address.") }}</li>
</ul> </ul>
What you can try:<br /> {{ $t("What you can try:") }}<br />
<ul> <ul>
<li>Retype the address.</li> <li>{{ $t("Retype the address.") }}</li>
<li><a href="#" class="go-back" @click="goBack()">Go back to the previous page.</a></li> <li><a href="#" class="go-back" @click="goBack()">{{ $t("Go back to the previous page.") }}</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -45,7 +45,7 @@
</div> </div>
<div v-if="false" class="my-3"> <div v-if="false" class="my-3">
<label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label> <label for="password" class="form-label">{{ $t("Password") }} <sup>{{ $t("Coming Soon") }}</sup></label>
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control"> <input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
</div> </div>
@@ -104,15 +104,16 @@
<!-- Uploader --> <!-- Uploader -->
<!-- url="/api/status-page/upload-logo" --> <!-- url="/api/status-page/upload-logo" -->
<ImageCropUpload v-model="showImageCropUpload" <ImageCropUpload
field="img" v-model="showImageCropUpload"
:width="128" field="img"
:height="128" :width="128"
:langType="$i18n.locale" :height="128"
img-format="png" :langType="$i18n.locale"
:noCircle="true" img-format="png"
:noSquare="false" :noCircle="true"
@crop-success="cropSuccess" :noSquare="false"
@crop-success="cropSuccess"
/> />
<!-- Title --> <!-- Title -->
@@ -281,22 +282,21 @@
<script> <script>
import axios from "axios"; import axios from "axios";
import PublicGroupList from "../components/PublicGroupList.vue";
import ImageCropUpload from "vue-image-crop-upload";
import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_PARTIAL_DOWN, UP } from "../util.ts";
import { useToast } from "vue-toastification";
import dayjs from "dayjs"; import dayjs from "dayjs";
import Favico from "favico.js"; import Favico from "favico.js";
import { getResBaseURL } from "../util-frontend";
import Confirm from "../components/Confirm.vue";
// import Prism Editor
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
// import highlighting library (you can use any library you want just return html string) // import highlighting library (you can use any library you want just return html string)
import { highlight, languages } from "prismjs/components/prism-core"; import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-css"; import "prismjs/components/prism-css";
import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles
import ImageCropUpload from "vue-image-crop-upload";
// import Prism Editor
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere
import { useToast } from "vue-toastification";
import Confirm from "../components/Confirm.vue";
import PublicGroupList from "../components/PublicGroupList.vue";
import { getResBaseURL } from "../util-frontend";
import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_PARTIAL_DOWN, UP } from "../util.ts";
const toast = useToast(); const toast = useToast();

View File

@@ -102,7 +102,7 @@ class Logger {
} }
else if (level === "DEBUG") { else if (level === "DEBUG") {
if (exports.isDev) { if (exports.isDev) {
console.debug(formattedMessage); console.log(formattedMessage);
} }
} }
else { else {

View File

@@ -113,7 +113,7 @@ class Logger {
console.error(formattedMessage); console.error(formattedMessage);
} else if (level === "DEBUG") { } else if (level === "DEBUG") {
if (isDev) { if (isDev) {
console.debug(formattedMessage); console.log(formattedMessage);
} }
} else { } else {
console.log(formattedMessage); console.log(formattedMessage);

View File

@@ -1,5 +1,9 @@
const { genSecret } = require("../src/util"); const { genSecret, DOWN } = require("../src/util");
const utilServerRewire = require("../server/util-server"); const utilServerRewire = require("../server/util-server");
const Discord = require("../server/notification-providers/discord");
const axios = require("axios");
jest.mock("axios");
describe("Test parseCertificateInfo", () => { describe("Test parseCertificateInfo", () => {
it("should handle undefined", async () => { it("should handle undefined", async () => {
@@ -164,3 +168,86 @@ describe("Test reset-password", () => {
}, 120000); }, 120000);
}); });
describe("Test Discord Notification Provider", () => {
const sendNotification = async (hostname, port, type) => {
const discordProvider = new Discord();
axios.post.mockResolvedValue({});
await discordProvider.send(
{
discordUsername: "Uptime Kuma",
discordWebhookUrl: "https://discord.com",
},
"test message",
{
type,
hostname,
port,
},
{
status: DOWN,
}
);
};
it("should send hostname for dns monitors", async () => {
const hostname = "discord.com";
await sendNotification(hostname, null, "dns");
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
hostname
);
});
it("should send hostname for ping monitors", async () => {
const hostname = "discord.com";
await sendNotification(hostname, null, "ping");
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
hostname
);
});
it("should send hostname for port monitors", async () => {
const hostname = "discord.com";
const port = 1337;
await sendNotification(hostname, port, "port");
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
`${hostname}:${port}`
);
});
it("should send hostname for steam monitors", async () => {
const hostname = "discord.com";
const port = 1337;
await sendNotification(hostname, port, "steam");
expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe(
`${hostname}:${port}`
);
});
});
describe("The function filterAndJoin", () => {
it("should join and array of strings to one string", () => {
const result = utilServerRewire.filterAndJoin(["one", "two", "three"]);
expect(result).toBe("onetwothree");
});
it("should join strings using a given connector", () => {
const result = utilServerRewire.filterAndJoin(["one", "two", "three"], "-");
expect(result).toBe("one-two-three");
});
it("should filter null, undefined and empty strings before joining", () => {
const result = utilServerRewire.filterAndJoin([undefined, "", "three"], "--");
expect(result).toBe("three");
});
it("should return an empty string if all parts are filtered out", () => {
const result = utilServerRewire.filterAndJoin([undefined, "", ""], "--");
expect(result).toBe("");
});
});