chore: enable formatting over the entire codebase in CI (#6655)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Frank Elsinga 2026-01-09 02:10:36 +01:00 committed by GitHub
parent 6658f2ce41
commit 0f61d7ee1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
422 changed files with 30899 additions and 27379 deletions

View File

@ -1,9 +1,5 @@
module.exports = { module.exports = {
ignorePatterns: [ ignorePatterns: ["test/*.js", "server/modules/*", "src/util.js"],
"test/*.js",
"server/modules/*",
"src/util.js"
],
root: true, root: true,
env: { env: {
browser: true, browser: true,
@ -23,94 +19,93 @@ module.exports = {
sourceType: "module", sourceType: "module",
requireConfigFile: false, requireConfigFile: false,
}, },
plugins: [ plugins: ["jsdoc", "@typescript-eslint"],
"jsdoc",
"@typescript-eslint",
],
rules: { rules: {
"yoda": "error", yoda: "error",
eqeqeq: [ "warn", "smart" ], eqeqeq: ["warn", "smart"],
"camelcase": [ "warn", { camelcase: [
"properties": "never", "warn",
"ignoreImports": true {
}], properties: "never",
"no-unused-vars": [ "warn", { ignoreImports: true,
"args": "none" },
}], ],
"no-unused-vars": [
"warn",
{
args: "none",
},
],
"vue/max-attributes-per-line": "off", "vue/max-attributes-per-line": "off",
"vue/singleline-html-element-content-newline": "off", "vue/singleline-html-element-content-newline": "off",
"vue/html-self-closing": "off", "vue/html-self-closing": "off",
"vue/require-component-is": "off", // not allow is="style" https://github.com/vuejs/eslint-plugin-vue/issues/462#issuecomment-430234675 "vue/require-component-is": "off", // not allow is="style" https://github.com/vuejs/eslint-plugin-vue/issues/462#issuecomment-430234675
"vue/attribute-hyphenation": "off", // This change noNL to "no-n-l" unexpectedly "vue/attribute-hyphenation": "off", // This change noNL to "no-n-l" unexpectedly
"vue/multi-word-component-names": "off", "vue/multi-word-component-names": "off",
"curly": "error", curly: "error",
"no-var": "error", "no-var": "error",
"no-throw-literal": "error", "no-throw-literal": "error",
"no-constant-condition": [ "error", { "no-constant-condition": [
"checkLoops": false, "error",
}], {
checkLoops: false,
},
],
//"no-console": "warn", //"no-console": "warn",
"no-extra-boolean-cast": "off", "no-extra-boolean-cast": "off",
"no-unneeded-ternary": "error", "no-unneeded-ternary": "error",
//"prefer-template": "error", //"prefer-template": "error",
"no-empty": [ "error", { "no-empty": [
"allowEmptyCatch": true "error",
}], {
allowEmptyCatch: true,
},
],
"no-control-regex": "off", "no-control-regex": "off",
"one-var": [ "error", "never" ], "one-var": ["error", "never"],
"max-statements-per-line": [ "error", { "max": 1 }], "max-statements-per-line": ["error", { max: 1 }],
"jsdoc/check-tag-names": [ "jsdoc/check-tag-names": [
"error", "error",
{ {
"definedTags": [ "link" ] definedTags: ["link"],
} },
], ],
"jsdoc/no-undefined-types": "off", "jsdoc/no-undefined-types": "off",
"jsdoc/no-defaults": [ "jsdoc/no-defaults": ["error", { noOptionalParamNames: true }],
"error",
{ "noOptionalParamNames": true }
],
"jsdoc/require-throws": "warn", "jsdoc/require-throws": "warn",
"jsdoc/require-jsdoc": [ "jsdoc/require-jsdoc": [
"error", "error",
{ {
"require": { require: {
"FunctionDeclaration": true, FunctionDeclaration: true,
"MethodDefinition": true, MethodDefinition: true,
} },
} },
], ],
"jsdoc/no-blank-block-descriptions": "error", "jsdoc/no-blank-block-descriptions": "error",
"jsdoc/require-returns-description": "warn", "jsdoc/require-returns-description": "warn",
"jsdoc/require-returns-check": [ "jsdoc/require-returns-check": ["error", { reportMissingReturnForUndefinedTypes: false }],
"error",
{ "reportMissingReturnForUndefinedTypes": false }
],
"jsdoc/require-returns": [ "jsdoc/require-returns": [
"warn", "warn",
{ {
"forceRequireReturn": true, forceRequireReturn: true,
"forceReturnsWithAsync": true forceReturnsWithAsync: true,
} },
], ],
"jsdoc/require-param-type": "warn", "jsdoc/require-param-type": "warn",
"jsdoc/require-param-description": "warn" "jsdoc/require-param-description": "warn",
}, },
"overrides": [ overrides: [
// Override for TypeScript // Override for TypeScript
{ {
"files": [ files: ["**/*.ts"],
"**/*.ts", extends: ["plugin:@typescript-eslint/recommended"],
], rules: {
extends: [
"plugin:@typescript-eslint/recommended",
],
"rules": {
"jsdoc/require-returns-type": "off", "jsdoc/require-returns-type": "off",
"jsdoc/require-param-type": "off", "jsdoc/require-param-type": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"prefer-const": "off", "prefer-const": "off",
} },
} },
] ],
}; };

View File

@ -38,9 +38,8 @@ jobs:
run: npm run lint-fix:style run: npm run lint-fix:style
continue-on-error: true continue-on-error: true
# TODO: disabled until we have agreed that this is the formatting that we want to enforce - name: Auto-format code with Prettier
# - name: Auto-format code with Prettier run: npm run fmt
# run: npm run fmt continue-on-error: true
# continue-on-error: true
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 - uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27

View File

@ -1,38 +1,2 @@
# Dependencies # language files
node_modules/ src/lang/*.json
# Build output
dist/
build/
# Data directories
data/
# Test output
test-results/
playwright-report/
private/
# Logs
*.log
npm-debug.log*
# OS files
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
# Lock files
package-lock.json
pnpm-lock.yaml
yarn.lock
# Generated files
*.min.js
*.min.css
# Docker
docker/

View File

@ -54,8 +54,7 @@ to review the appropriate one for your contribution.
[**PLEASE SEE OUR SECURITY POLICY.**](SECURITY.md) [**PLEASE SEE OUR SECURITY POLICY.**](SECURITY.md)
[advisory]: https://github.com/louislam/uptime-kuma/security/advisories/new [advisory]: https://github.com/louislam/uptime-kuma/security/advisories/new
[issue]: [issue]: https://github.com/louislam/uptime-kuma/issues/new?template=security_issue.yml
https://github.com/louislam/uptime-kuma/issues/new?template=security_issue.yml
</p> </p>
</details> </details>
@ -65,7 +64,6 @@ to review the appropriate one for your contribution.
If you come across a bug and think you can solve, we appreciate your work. If you come across a bug and think you can solve, we appreciate your work.
Please make sure that you follow these rules: Please make sure that you follow these rules:
- keep the PR as small as possible, fix only one thing at a time => keeping it - keep the PR as small as possible, fix only one thing at a time => keeping it
reviewable reviewable
- test that your code does what you claim it does. - test that your code does what you claim it does.
@ -84,7 +82,6 @@ to review the appropriate one for your contribution.
We use Weblate to localise this project into many languages. If you want to help translate Uptime Kuma into your language, please see [these instructions on how to translate using Weblate](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md). We use Weblate to localise this project into many languages. If you want to help translate Uptime Kuma into your language, please see [these instructions on how to translate using Weblate](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md).
There are some cases where a change cannot be done directly in Weblate and requires a PR: There are some cases where a change cannot be done directly in Weblate and requires a PR:
- A text may not yet be localisable. In this case, **adding a new language key** via `{{ $t("Translation key") }}` or [`<i18n-t keypath="Translation key">`](https://vue-i18n.intlify.dev/guide/advanced/component.html) might be necessary. - A text may not yet be localisable. In this case, **adding a new language key** via `{{ $t("Translation key") }}` or [`<i18n-t keypath="Translation key">`](https://vue-i18n.intlify.dev/guide/advanced/component.html) might be necessary.
- Language keys need to be **added to `en.json`** to appear in Weblate. If this has not been done, a PR is appreciated. - Language keys need to be **added to `en.json`** to appear in Weblate. If this has not been done, a PR is appreciated.
- **Adding a new language** requires creating a new file. See [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md). - **Adding a new language** requires creating a new file. See [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md).
@ -98,7 +95,6 @@ to review the appropriate one for your contribution.
<p> <p>
To set up a new notification provider these files need to be modified/created: To set up a new notification provider these files need to be modified/created:
- `server/notification-providers/PROVIDER_NAME.js` is where the heart of the - `server/notification-providers/PROVIDER_NAME.js` is where the heart of the
notification provider lives. notification provider lives.
@ -135,7 +131,6 @@ to review the appropriate one for your contribution.
translations (`{{ $t("Translation key") }}`, translations (`{{ $t("Translation key") }}`,
[`i18n-t keypath="Translation key">`](https://vue-i18n.intlify.dev/guide/advanced/component.html)) [`i18n-t keypath="Translation key">`](https://vue-i18n.intlify.dev/guide/advanced/component.html))
in `src/lang/en.json` to enable our translators to translate this in `src/lang/en.json` to enable our translators to translate this
- `src/components/notifications/index.js` is where the frontend of the - `src/components/notifications/index.js` is where the frontend of the
provider needs to be registered. _If you have an idea how we can skip this provider needs to be registered. _If you have an idea how we can skip this
step, we would love to hear about it ^^_ step, we would love to hear about it ^^_
@ -147,7 +142,6 @@ to review the appropriate one for your contribution.
To make sure you have tested the notification provider, please include To make sure you have tested the notification provider, please include
screenshots of the following events in the pull-request description: screenshots of the following events in the pull-request description:
- `UP`/`DOWN` - `UP`/`DOWN`
- Certificate Expiry via <https://expired.badssl.com/> - Certificate Expiry via <https://expired.badssl.com/>
- Domain Expiry via <https://google.com/> and a larger time set - Domain Expiry via <https://google.com/> and a larger time set
@ -159,7 +153,7 @@ to review the appropriate one for your contribution.
```md ```md
| Event | Before | After | | Event | Before | After |
|--------------------|-----------------------|----------------------| | ------------------ | --------------------- | -------------------- |
| `UP` | ![Before](image-link) | ![After](image-link) | | `UP` | ![Before](image-link) | ![After](image-link) |
| `DOWN` | ![Before](image-link) | ![After](image-link) | | `DOWN` | ![Before](image-link) | ![After](image-link) |
| Certificate-expiry | ![Before](image-link) | ![After](image-link) | | Certificate-expiry | ![Before](image-link) | ![After](image-link) |
@ -177,7 +171,6 @@ to review the appropriate one for your contribution.
<p> <p>
To set up a new notification provider these files need to be modified/created: To set up a new notification provider these files need to be modified/created:
- `server/monitor-types/MONITORING_TYPE.js` is the core of each monitor. - `server/monitor-types/MONITORING_TYPE.js` is the core of each monitor.
The `async check(...)`-function should: The `async check(...)`-function should:
- in the happy-path: set `heartbeat.msg` to a successful message and set `heartbeat.status = UP` - in the happy-path: set `heartbeat.msg` to a successful message and set `heartbeat.status = UP`
@ -220,7 +213,6 @@ to review the appropriate one for your contribution.
<p> <p>
Contributing is easy and fun. We will guide you through the process: Contributing is easy and fun. We will guide you through the process:
1. **Fork** the [Uptime-Kuma repository](https://github.com/louislam/uptime-kuma/) and **clone** it to your local machine. 1. **Fork** the [Uptime-Kuma repository](https://github.com/louislam/uptime-kuma/) and **clone** it to your local machine.
2. **Create a new branch** for your changes (e.g., `signal-notification-provider`). 2. **Create a new branch** for your changes (e.g., `signal-notification-provider`).
3. **Make your changes** and **commit** them with a clear message. 3. **Make your changes** and **commit** them with a clear message.
@ -235,7 +227,6 @@ to review the appropriate one for your contribution.
A PR should remain in **draft status** until all tasks are completed. A PR should remain in **draft status** until all tasks are completed.
Only change the status to **Ready for Review** when: Only change the status to **Ready for Review** when:
- You have implemented all planned changes. - You have implemented all planned changes.
- Your code is fully tested and ready for review. - Your code is fully tested and ready for review.
- You have updated or created the necessary tests. - You have updated or created the necessary tests.
@ -248,7 +239,6 @@ to review the appropriate one for your contribution.
- Merging multiple issues by a huge PR is more difficult to review and causes - Merging multiple issues by a huge PR is more difficult to review and causes
conflicts with other PRs. Please conflicts with other PRs. Please
- (if possible) **create one PR for one issue** or - (if possible) **create one PR for one issue** or
- (if not possible) **explain which issues a PR addresses and why this PR - (if not possible) **explain which issues a PR addresses and why this PR
should not be broken apart** should not be broken apart**
@ -269,6 +259,7 @@ to review the appropriate one for your contribution.
### Continuous Integration ### Continuous Integration
All pull requests must pass our continuous integration checks. These checks include: All pull requests must pass our continuous integration checks. These checks include:
- **Linting**: We use ESLint and Stylelint for code quality checks. You can run the linter locally with `npm run lint`. - **Linting**: We use ESLint and Stylelint for code quality checks. You can run the linter locally with `npm run lint`.
- **Formatting**: We use Prettier for code formatting. You can format your code with `npm run fmt` (or CI will do this for you) - **Formatting**: We use Prettier for code formatting. You can format your code with `npm run fmt` (or CI will do this for you)
- **Testing**: We use Playwright for end-to-end tests and have a suite of backend tests. You can run the tests locally with `npm test`. - **Testing**: We use Playwright for end-to-end tests and have a suite of backend tests. You can run the tests locally with `npm test`.
@ -297,13 +288,11 @@ you can finally start the app. The goal is to make the Uptime Kuma installation
as easy as installing a mobile app. as easy as installing a mobile app.
- Easy to install for non-Docker users - Easy to install for non-Docker users
- no native build dependency is needed (for `x86_64`/`armv7`/`arm64`) - no native build dependency is needed (for `x86_64`/`armv7`/`arm64`)
- no extra configuration and - no extra configuration and
- no extra effort required to get it running - no extra effort required to get it running
- Single container for Docker users - Single container for Docker users
- no complex docker-compose file - no complex docker-compose file
- mapping the volume and exposing the port should be the only requirements - mapping the volume and exposing the port should be the only requirements
@ -480,18 +469,16 @@ We have a few procedures we follow. These are documented here:
- <details><summary><b>Set up a Docker Builder</b> (click to expand)</summary> - <details><summary><b>Set up a Docker Builder</b> (click to expand)</summary>
<p> <p>
- amd64, armv7 using local. - amd64, armv7 using local.
- arm64 using remote arm64 cpu, as the emulator is too slow and can no longer - arm64 using remote arm64 cpu, as the emulator is too slow and can no longer
pass the `npm ci` command. pass the `npm ci` command.
1. Add the public key to the remote server. 1. Add the public key to the remote server.
2. Add the remote context. The remote machine must be arm64 and installed 2. Add the remote context. The remote machine must be arm64 and installed
Docker CE. Docker CE.
```bash ```bash
docker context create oracle-arm64-jp --docker "host=ssh://root@100.107.174.88" docker context create oracle-arm64-jp --docker "host=ssh://root@100.107.174.88"
``` ```
3. Create a new builder. 3. Create a new builder.
@ -515,7 +502,6 @@ We have a few procedures we follow. These are documented here:
- <details><summary><b>Release</b> (click to expand)</summary> - <details><summary><b>Release</b> (click to expand)</summary>
<p> <p>
1. Draft a release note 1. Draft a release note
2. Make sure the repo is cleared 2. Make sure the repo is cleared
3. If the healthcheck is updated, remember to re-compile it: 3. If the healthcheck is updated, remember to re-compile it:
@ -528,7 +514,6 @@ We have a few procedures we follow. These are documented here:
9. Deploy to the demo server: `npm run deploy-demo-server` 9. Deploy to the demo server: `npm run deploy-demo-server`
These Items need to be checked: These Items need to be checked:
- [ ] Check all tags is fine on - [ ] Check all tags is fine on
<https://hub.docker.com/r/louislam/uptime-kuma/tags> <https://hub.docker.com/r/louislam/uptime-kuma/tags>
- [ ] Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64 / - [ ] Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64 /
@ -540,7 +525,6 @@ We have a few procedures we follow. These are documented here:
- <details><summary><b>Release Beta</b> (click to expand)</summary> - <details><summary><b>Release Beta</b> (click to expand)</summary>
<p> <p>
1. Draft a release note, check `This is a pre-release` 1. Draft a release note, check `This is a pre-release`
2. Make sure the repo is cleared 2. Make sure the repo is cleared
3. `npm run release-beta` with env vars: `VERSION` and `GITHUB_TOKEN` 3. `npm run release-beta` with env vars: `VERSION` and `GITHUB_TOKEN`

View File

@ -6,7 +6,7 @@
Uptime Kuma is an easy-to-use self-hosted monitoring tool. Uptime Kuma is an easy-to-use self-hosted monitoring tool.
<a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma?style=flat" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/2?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Open%20Collective%20Backers&color=brightgreen" /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/stars/louislam/uptime-kuma?style=flat" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/pulls/louislam/uptime-kuma" /></a> <a target="_blank" href="https://hub.docker.com/r/louislam/uptime-kuma"><img src="https://img.shields.io/docker/v/louislam/uptime-kuma/2?label=docker%20image%20ver." /></a> <a target="_blank" href="https://github.com/louislam/uptime-kuma"><img src="https://img.shields.io/github/last-commit/louislam/uptime-kuma" /></a> <a target="_blank" href="https://opencollective.com/uptime-kuma"><img src="https://opencollective.com/uptime-kuma/total/badge.svg?label=Open%20Collective%20Backers&color=brightgreen" /></a>
[![GitHub Sponsors](https://img.shields.io/github/sponsors/louislam?label=GitHub%20Sponsors)](https://github.com/sponsors/louislam) <a href="https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/"> [![GitHub Sponsors](https://img.shields.io/github/sponsors/louislam?label=GitHub%20Sponsors)](https://github.com/sponsors/louislam) <a href="https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/">
<img src="https://weblate.kuma.pet/widgets/uptime-kuma/-/svg-badge.svg" alt="Translation status" /> <img src="https://weblate.kuma.pet/widgets/uptime-kuma/-/svg-badge.svg" alt="Translation status" />
</a> </a>
@ -45,6 +45,7 @@ cd uptime-kuma
curl -o compose.yaml https://raw.githubusercontent.com/louislam/uptime-kuma/master/compose.yaml curl -o compose.yaml https://raw.githubusercontent.com/louislam/uptime-kuma/master/compose.yaml
docker compose up -d docker compose up -d
``` ```
Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001). Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001).
> [!WARNING] > [!WARNING]
@ -55,6 +56,7 @@ Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001
```bash ```bash
docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:2 docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:2
``` ```
Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001). Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001).
If you want to limit exposure to localhost only: If you want to limit exposure to localhost only:
@ -63,8 +65,6 @@ If you want to limit exposure to localhost only:
docker run ... -p 127.0.0.1:3001:3001 ... docker run ... -p 127.0.0.1:3001:3001 ...
``` ```
### 💪🏻 Non-Docker ### 💪🏻 Non-Docker
Requirements: Requirements:
@ -93,6 +93,7 @@ npm install pm2 -g && pm2 install pm2-logrotate
# Start Server # Start Server
pm2 start server/server.js --name uptime-kuma pm2 start server/server.js --name uptime-kuma
``` ```
Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001). Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001).
More useful PM2 Commands More useful PM2 Commands

View File

@ -10,7 +10,7 @@
- Do not report any upstream dependency issues / scan result by any tools. It will be closed immediately without explanations. Unless you have PoC to prove that the upstream issue affected Uptime Kuma. - Do not report any upstream dependency issues / scan result by any tools. It will be closed immediately without explanations. Unless you have PoC to prove that the upstream issue affected Uptime Kuma.
- Do not use the public issue tracker or discuss it in public as it will cause - Do not use the public issue tracker or discuss it in public as it will cause
more damage. more damage.
## Do you accept other 3rd-party bug bounty platforms? ## Do you accept other 3rd-party bug bounty platforms?

View File

@ -22,10 +22,11 @@ export default defineConfig({
// Reporter to use // Reporter to use
reporter: [ reporter: [
[ [
"html", { "html",
{
outputFolder: "../private/playwright-report", outputFolder: "../private/playwright-report",
open: "never", open: "never",
} },
], ],
], ],
@ -47,7 +48,7 @@ export default defineConfig({
{ {
name: "specs", name: "specs",
use: { ...devices["Desktop Chrome"] }, use: { ...devices["Desktop Chrome"] },
dependencies: [ "run-once setup" ], dependencies: ["run-once setup"],
}, },
/* /*
{ {

View File

@ -15,13 +15,13 @@ export default defineConfig({
port: 3000, port: 3000,
}, },
define: { define: {
"FRONTEND_VERSION": JSON.stringify(process.env.npm_package_version), FRONTEND_VERSION: JSON.stringify(process.env.npm_package_version),
"process.env": {}, "process.env": {},
}, },
plugins: [ plugins: [
vue(), vue(),
visualizer({ visualizer({
filename: "tmp/dist-stats.html" filename: "tmp/dist-stats.html",
}), }),
viteCompression({ viteCompression({
algorithm: "gzip", algorithm: "gzip",
@ -40,21 +40,19 @@ export default defineConfig({
], ],
css: { css: {
postcss: { postcss: {
"parser": postCssScss, parser: postCssScss,
"map": false, map: false,
"plugins": [ postcssRTLCSS ] plugins: [postcssRTLCSS],
} },
}, },
build: { build: {
commonjsOptions: { commonjsOptions: {
include: [ /.js$/ ], include: [/.js$/],
}, },
rollupOptions: { rollupOptions: {
output: { output: {
manualChunks(id, { getModuleInfo, getModuleIds }) { manualChunks(id, { getModuleInfo, getModuleIds }) {},
},
}
}
}, },
} },
}); });

View File

@ -39,7 +39,7 @@ async function createTables() {
table.integer("user_id").unsigned().notNullable(); table.integer("user_id").unsigned().notNullable();
table.string("protocol", 10).notNullable(); table.string("protocol", 10).notNullable();
table.string("host", 255).notNullable(); table.string("host", 255).notNullable();
table.smallint("port").notNullable(); // TODO: Maybe a issue with MariaDB, need migration to int table.smallint("port").notNullable(); // TODO: Maybe a issue with MariaDB, need migration to int
table.boolean("auth").notNullable(); table.boolean("auth").notNullable();
table.string("username", 255).nullable(); table.string("username", 255).nullable();
table.string("password", 255).nullable(); table.string("password", 255).nullable();
@ -67,10 +67,7 @@ async function createTables() {
table.increments("id"); table.increments("id");
table.string("name", 150); table.string("name", 150);
table.boolean("active").notNullable().defaultTo(true); table.boolean("active").notNullable().defaultTo(true);
table.integer("user_id").unsigned() table.integer("user_id").unsigned().references("id").inTable("user").onDelete("SET NULL").onUpdate("CASCADE");
.references("id").inTable("user")
.onDelete("SET NULL")
.onUpdate("CASCADE");
table.integer("interval").notNullable().defaultTo(20); table.integer("interval").notNullable().defaultTo(20);
table.text("url"); table.text("url");
table.string("type", 20); table.string("type", 20);
@ -83,7 +80,7 @@ async function createTables() {
table.boolean("ignore_tls").notNullable().defaultTo(false); table.boolean("ignore_tls").notNullable().defaultTo(false);
table.boolean("upside_down").notNullable().defaultTo(false); table.boolean("upside_down").notNullable().defaultTo(false);
table.integer("maxredirects").notNullable().defaultTo(10); table.integer("maxredirects").notNullable().defaultTo(10);
table.text("accepted_statuscodes_json").notNullable().defaultTo("[\"200-299\"]"); table.text("accepted_statuscodes_json").notNullable().defaultTo('["200-299"]');
table.string("dns_resolve_type", 5); table.string("dns_resolve_type", 5);
table.string("dns_resolve_server", 255); table.string("dns_resolve_server", 255);
table.string("dns_last_result", 255); table.string("dns_last_result", 255);
@ -94,11 +91,9 @@ async function createTables() {
table.text("headers").defaultTo(null); table.text("headers").defaultTo(null);
table.text("basic_auth_user").defaultTo(null); table.text("basic_auth_user").defaultTo(null);
table.text("basic_auth_pass").defaultTo(null); table.text("basic_auth_pass").defaultTo(null);
table.integer("docker_host").unsigned() table.integer("docker_host").unsigned().references("id").inTable("docker_host");
.references("id").inTable("docker_host");
table.string("docker_container", 255); table.string("docker_container", 255);
table.integer("proxy_id").unsigned() table.integer("proxy_id").unsigned().references("id").inTable("proxy");
.references("id").inTable("proxy");
table.boolean("expiry_notification").defaultTo(true); table.boolean("expiry_notification").defaultTo(true);
table.text("mqtt_topic"); table.text("mqtt_topic");
table.string("mqtt_success_message", 255); table.string("mqtt_success_message", 255);
@ -130,8 +125,12 @@ async function createTables() {
await knex.schema.createTable("heartbeat", (table) => { await knex.schema.createTable("heartbeat", (table) => {
table.increments("id"); table.increments("id");
table.boolean("important").notNullable().defaultTo(false); table.boolean("important").notNullable().defaultTo(false);
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.smallint("status").notNullable(); table.smallint("status").notNullable();
@ -143,9 +142,9 @@ async function createTables() {
table.integer("down_count").notNullable().defaultTo(0); table.integer("down_count").notNullable().defaultTo(0);
table.index("important"); table.index("important");
table.index([ "monitor_id", "time" ], "monitor_time_index"); table.index(["monitor_id", "time"], "monitor_time_index");
table.index("monitor_id"); table.index("monitor_id");
table.index([ "monitor_id", "important", "time" ], "monitor_important_time_index"); table.index(["monitor_id", "important", "time"], "monitor_important_time_index");
}); });
// incident // incident
@ -166,10 +165,7 @@ async function createTables() {
table.increments("id"); table.increments("id");
table.string("title", 150).notNullable(); table.string("title", 150).notNullable();
table.text("description").notNullable(); table.text("description").notNullable();
table.integer("user_id").unsigned() table.integer("user_id").unsigned().references("id").inTable("user").onDelete("SET NULL").onUpdate("CASCADE");
.references("id").inTable("user")
.onDelete("SET NULL")
.onUpdate("CASCADE");
table.boolean("active").notNullable().defaultTo(true); table.boolean("active").notNullable().defaultTo(true);
table.string("strategy", 50).notNullable().defaultTo("single"); table.string("strategy", 50).notNullable().defaultTo("single");
table.datetime("start_date"); table.datetime("start_date");
@ -181,7 +177,7 @@ async function createTables() {
table.integer("interval_day"); table.integer("interval_day");
table.index("active"); table.index("active");
table.index([ "strategy", "active" ], "manual_active"); table.index(["strategy", "active"], "manual_active");
table.index("user_id", "maintenance_user_id"); table.index("user_id", "maintenance_user_id");
}); });
@ -209,13 +205,21 @@ async function createTables() {
await knex.schema.createTable("maintenance_status_page", (table) => { await knex.schema.createTable("maintenance_status_page", (table) => {
table.increments("id"); table.increments("id");
table.integer("status_page_id").unsigned().notNullable() table
.references("id").inTable("status_page") .integer("status_page_id")
.unsigned()
.notNullable()
.references("id")
.inTable("status_page")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("maintenance_id").unsigned().notNullable() table
.references("id").inTable("maintenance") .integer("maintenance_id")
.unsigned()
.notNullable()
.references("id")
.inTable("maintenance")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
}); });
@ -223,8 +227,12 @@ async function createTables() {
// maintenance_timeslot // maintenance_timeslot
await knex.schema.createTable("maintenance_timeslot", (table) => { await knex.schema.createTable("maintenance_timeslot", (table) => {
table.increments("id"); table.increments("id");
table.integer("maintenance_id").unsigned().notNullable() table
.references("id").inTable("maintenance") .integer("maintenance_id")
.unsigned()
.notNullable()
.references("id")
.inTable("maintenance")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.datetime("start_date").notNullable(); table.datetime("start_date").notNullable();
@ -232,35 +240,51 @@ async function createTables() {
table.boolean("generated_next").defaultTo(false); table.boolean("generated_next").defaultTo(false);
table.index("maintenance_id"); table.index("maintenance_id");
table.index([ "maintenance_id", "start_date", "end_date" ], "active_timeslot_index"); table.index(["maintenance_id", "start_date", "end_date"], "active_timeslot_index");
table.index("generated_next", "generated_next_index"); table.index("generated_next", "generated_next_index");
}); });
// monitor_group // monitor_group
await knex.schema.createTable("monitor_group", (table) => { await knex.schema.createTable("monitor_group", (table) => {
table.increments("id"); table.increments("id");
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("group_id").unsigned().notNullable() table
.references("id").inTable("group") .integer("group_id")
.unsigned()
.notNullable()
.references("id")
.inTable("group")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("weight").notNullable().defaultTo(1000); table.integer("weight").notNullable().defaultTo(1000);
table.boolean("send_url").notNullable().defaultTo(false); table.boolean("send_url").notNullable().defaultTo(false);
table.index([ "monitor_id", "group_id" ], "fk"); table.index(["monitor_id", "group_id"], "fk");
}); });
// monitor_maintenance // monitor_maintenance
await knex.schema.createTable("monitor_maintenance", (table) => { await knex.schema.createTable("monitor_maintenance", (table) => {
table.increments("id"); table.increments("id");
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("maintenance_id").unsigned().notNullable() table
.references("id").inTable("maintenance") .integer("maintenance_id")
.unsigned()
.notNullable()
.references("id")
.inTable("maintenance")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
@ -280,17 +304,25 @@ async function createTables() {
// monitor_notification // monitor_notification
await knex.schema.createTable("monitor_notification", (table) => { await knex.schema.createTable("monitor_notification", (table) => {
table.increments("id").unsigned(); // TODO: no auto increment???? table.increments("id").unsigned(); // TODO: no auto increment????
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("notification_id").unsigned().notNullable() table
.references("id").inTable("notification") .integer("notification_id")
.unsigned()
.notNullable()
.references("id")
.inTable("notification")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.index([ "monitor_id", "notification_id" ], "monitor_notification_index"); table.index(["monitor_id", "notification_id"], "monitor_notification_index");
}); });
// tag // tag
@ -304,12 +336,20 @@ async function createTables() {
// monitor_tag // monitor_tag
await knex.schema.createTable("monitor_tag", (table) => { await knex.schema.createTable("monitor_tag", (table) => {
table.increments("id"); table.increments("id");
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("tag_id").unsigned().notNullable() table
.references("id").inTable("tag") .integer("tag_id")
.unsigned()
.notNullable()
.references("id")
.inTable("tag")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.text("value"); table.text("value");
@ -318,8 +358,12 @@ async function createTables() {
// monitor_tls_info // monitor_tls_info
await knex.schema.createTable("monitor_tls_info", (table) => { await knex.schema.createTable("monitor_tls_info", (table) => {
table.increments("id"); table.increments("id");
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.text("info_json"); table.text("info_json");
@ -331,8 +375,8 @@ async function createTables() {
table.string("type", 50).notNullable(); table.string("type", 50).notNullable();
table.integer("monitor_id").unsigned().notNullable(); table.integer("monitor_id").unsigned().notNullable();
table.integer("days").notNullable(); table.integer("days").notNullable();
table.unique([ "type", "monitor_id", "days" ]); table.unique(["type", "monitor_id", "days"]);
table.index([ "type", "monitor_id", "days" ], "good_index"); table.index(["type", "monitor_id", "days"], "good_index");
}); });
// setting // setting
@ -346,16 +390,19 @@ async function createTables() {
// status_page_cname // status_page_cname
await knex.schema.createTable("status_page_cname", (table) => { await knex.schema.createTable("status_page_cname", (table) => {
table.increments("id"); table.increments("id");
table.integer("status_page_id").unsigned() table
.references("id").inTable("status_page") .integer("status_page_id")
.unsigned()
.references("id")
.inTable("status_page")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.string("domain").notNullable().unique().collate("utf8_general_ci"); table.string("domain").notNullable().unique().collate("utf8_general_ci");
}); });
/********************* /*********************
* Converted Patch here * Converted Patch here
*********************/ *********************/
// 2023-06-30-1348-http-body-encoding.js // 2023-06-30-1348-http-body-encoding.js
// ALTER TABLE monitor ADD http_body_encoding VARCHAR(25); // ALTER TABLE monitor ADD http_body_encoding VARCHAR(25);
@ -396,8 +443,12 @@ async function createTables() {
table.increments("id").primary(); table.increments("id").primary();
table.string("key", 255).notNullable(); table.string("key", 255).notNullable();
table.string("name", 255).notNullable(); table.string("name", 255).notNullable();
table.integer("user_id").unsigned().notNullable() table
.references("id").inTable("user") .integer("user_id")
.unsigned()
.notNullable()
.references("id")
.inTable("user")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.dateTime("created_date").defaultTo(knex.fn.now()).notNullable(); table.dateTime("created_date").defaultTo(knex.fn.now()).notNullable();
@ -430,13 +481,11 @@ async function createTables() {
ALTER TABLE maintenance ADD timezone VARCHAR(255); ALTER TABLE maintenance ADD timezone VARCHAR(255);
ALTER TABLE maintenance ADD duration INTEGER; ALTER TABLE maintenance ADD duration INTEGER;
*/ */
await knex.schema await knex.schema.dropTableIfExists("maintenance_timeslot").table("maintenance", function (table) {
.dropTableIfExists("maintenance_timeslot") table.text("cron");
.table("maintenance", function (table) { table.string("timezone", 255);
table.text("cron"); table.integer("duration");
table.string("timezone", 255); });
table.integer("duration");
});
// 2023-06-30-1413-add-parent-monitor.js. // 2023-06-30-1413-add-parent-monitor.js.
/* /*
@ -444,10 +493,7 @@ async function createTables() {
ADD parent INTEGER REFERENCES [monitor] ([id]) ON DELETE SET NULL ON UPDATE CASCADE; ADD parent INTEGER REFERENCES [monitor] ([id]) ON DELETE SET NULL ON UPDATE CASCADE;
*/ */
await knex.schema.table("monitor", function (table) { await knex.schema.table("monitor", function (table) {
table.integer("parent").unsigned() table.integer("parent").unsigned().references("id").inTable("monitor").onDelete("SET NULL").onUpdate("CASCADE");
.references("id").inTable("monitor")
.onDelete("SET NULL")
.onUpdate("CASCADE");
}); });
/* /*

View File

@ -3,39 +3,41 @@ exports.up = function (knex) {
.createTable("stat_minutely", function (table) { .createTable("stat_minutely", function (table) {
table.increments("id"); table.increments("id");
table.comment("This table contains the minutely aggregate statistics for each monitor"); table.comment("This table contains the minutely aggregate statistics for each monitor");
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("timestamp") table.integer("timestamp").notNullable().comment("Unix timestamp rounded down to the nearest minute");
.notNullable()
.comment("Unix timestamp rounded down to the nearest minute");
table.float("ping").notNullable().comment("Average ping in milliseconds"); table.float("ping").notNullable().comment("Average ping in milliseconds");
table.smallint("up").notNullable(); table.smallint("up").notNullable();
table.smallint("down").notNullable(); table.smallint("down").notNullable();
table.unique([ "monitor_id", "timestamp" ]); table.unique(["monitor_id", "timestamp"]);
}) })
.createTable("stat_daily", function (table) { .createTable("stat_daily", function (table) {
table.increments("id"); table.increments("id");
table.comment("This table contains the daily aggregate statistics for each monitor"); table.comment("This table contains the daily aggregate statistics for each monitor");
table.integer("monitor_id").unsigned().notNullable() table
.references("id").inTable("monitor") .integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE") .onDelete("CASCADE")
.onUpdate("CASCADE"); .onUpdate("CASCADE");
table.integer("timestamp") table.integer("timestamp").notNullable().comment("Unix timestamp rounded down to the nearest day");
.notNullable()
.comment("Unix timestamp rounded down to the nearest day");
table.float("ping").notNullable().comment("Average ping in milliseconds"); table.float("ping").notNullable().comment("Average ping in milliseconds");
table.smallint("up").notNullable(); table.smallint("up").notNullable();
table.smallint("down").notNullable(); table.smallint("down").notNullable();
table.unique([ "monitor_id", "timestamp" ]); table.unique(["monitor_id", "timestamp"]);
}); });
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.dropTable("stat_minutely").dropTable("stat_daily");
.dropTable("stat_minutely")
.dropTable("stat_daily");
}; };

View File

@ -1,16 +1,13 @@
exports.up = function (knex) { exports.up = function (knex) {
// Add new column heartbeat.end_time // Add new column heartbeat.end_time
return knex.schema return knex.schema.alterTable("heartbeat", function (table) {
.alterTable("heartbeat", function (table) { table.datetime("end_time").nullable().defaultTo(null);
table.datetime("end_time").nullable().defaultTo(null); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
// Rename heartbeat.start_time to heartbeat.time // Rename heartbeat.start_time to heartbeat.time
return knex.schema return knex.schema.alterTable("heartbeat", function (table) {
.alterTable("heartbeat", function (table) { table.dropColumn("end_time");
table.dropColumn("end_time"); });
});
}; };

View File

@ -1,15 +1,12 @@
exports.up = function (knex) { exports.up = function (knex) {
// Add new column heartbeat.retries // Add new column heartbeat.retries
return knex.schema return knex.schema.alterTable("heartbeat", function (table) {
.alterTable("heartbeat", function (table) { table.integer("retries").notNullable().defaultTo(0);
table.integer("retries").notNullable().defaultTo(0); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("heartbeat", function (table) {
.alterTable("heartbeat", function (table) { table.dropColumn("retries");
table.dropColumn("retries"); });
});
}; };

View File

@ -1,16 +1,13 @@
exports.up = function (knex) { exports.up = function (knex) {
// Add new column monitor.mqtt_check_type // Add new column monitor.mqtt_check_type
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("mqtt_check_type", 255).notNullable().defaultTo("keyword");
table.string("mqtt_check_type", 255).notNullable().defaultTo("keyword"); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
// Drop column monitor.mqtt_check_type // Drop column monitor.mqtt_check_type
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.dropColumn("mqtt_check_type");
table.dropColumn("mqtt_check_type"); });
});
}; };

View File

@ -1,14 +1,12 @@
exports.up = function (knex) { exports.up = function (knex) {
// update monitor.push_token to 32 length // update monitor.push_token to 32 length
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("push_token", 32).alter();
table.string("push_token", 32).alter(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("push_token", 20).alter();
table.string("push_token", 20).alter(); });
});
}; };

View File

@ -5,9 +5,14 @@ exports.up = function (knex) {
table.string("name", 255).notNullable(); table.string("name", 255).notNullable();
table.string("url", 255).notNullable(); table.string("url", 255).notNullable();
table.integer("user_id").unsigned(); table.integer("user_id").unsigned();
}).alterTable("monitor", function (table) { })
.alterTable("monitor", function (table) {
// Add new column monitor.remote_browser // Add new column monitor.remote_browser
table.integer("remote_browser").nullable().defaultTo(null).unsigned() table
.integer("remote_browser")
.nullable()
.defaultTo(null)
.unsigned()
.index() .index()
.references("id") .references("id")
.inTable("remote_browser"); .inTable("remote_browser");

View File

@ -1,8 +1,7 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("status_page", function (table) {
.alterTable("status_page", function (table) { table.integer("auto_refresh_interval").defaultTo(300).unsigned();
table.integer("auto_refresh_interval").defaultTo(300).unsigned(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,14 +1,29 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema
.alterTable("stat_daily", function (table) { .alterTable("stat_daily", function (table) {
table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds"); table
table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds"); .float("ping_min")
.notNullable()
.defaultTo(0)
.comment("Minimum ping during this period in milliseconds");
table
.float("ping_max")
.notNullable()
.defaultTo(0)
.comment("Maximum ping during this period in milliseconds");
}) })
.alterTable("stat_minutely", function (table) { .alterTable("stat_minutely", function (table) {
table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds"); table
table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds"); .float("ping_min")
.notNullable()
.defaultTo(0)
.comment("Minimum ping during this period in milliseconds");
table
.float("ping_max")
.notNullable()
.defaultTo(0)
.comment("Maximum ping during this period in milliseconds");
}); });
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,26 +1,26 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.createTable("stat_hourly", function (table) {
.createTable("stat_hourly", function (table) { table.increments("id");
table.increments("id"); table.comment("This table contains the hourly aggregate statistics for each monitor");
table.comment("This table contains the hourly aggregate statistics for each monitor"); table
table.integer("monitor_id").unsigned().notNullable() .integer("monitor_id")
.references("id").inTable("monitor") .unsigned()
.onDelete("CASCADE") .notNullable()
.onUpdate("CASCADE"); .references("id")
table.integer("timestamp") .inTable("monitor")
.notNullable() .onDelete("CASCADE")
.comment("Unix timestamp rounded down to the nearest hour"); .onUpdate("CASCADE");
table.float("ping").notNullable().comment("Average ping in milliseconds"); table.integer("timestamp").notNullable().comment("Unix timestamp rounded down to the nearest hour");
table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds"); table.float("ping").notNullable().comment("Average ping in milliseconds");
table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds"); table.float("ping_min").notNullable().defaultTo(0).comment("Minimum ping during this period in milliseconds");
table.smallint("up").notNullable(); table.float("ping_max").notNullable().defaultTo(0).comment("Maximum ping during this period in milliseconds");
table.smallint("down").notNullable(); table.smallint("up").notNullable();
table.smallint("down").notNullable();
table.unique([ "monitor_id", "timestamp" ]); table.unique(["monitor_id", "timestamp"]);
}); });
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.dropTable("stat_hourly");
.dropTable("stat_hourly");
}; };

View File

@ -9,7 +9,6 @@ exports.up = function (knex) {
.alterTable("stat_hourly", function (table) { .alterTable("stat_hourly", function (table) {
table.text("extras").defaultTo(null).comment("Extra statistics during this time period"); table.text("extras").defaultTo(null).comment("Extra statistics during this time period");
}); });
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,10 +1,9 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("snmp_oid").defaultTo(null);
table.string("snmp_oid").defaultTo(null); table.enum("snmp_version", ["1", "2c", "3"]).defaultTo("2c");
table.enum("snmp_version", [ "1", "2c", "3" ]).defaultTo("2c"); table.string("json_path_operator").defaultTo(null);
table.string("json_path_operator").defaultTo(null); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,13 +1,11 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.boolean("cache_bust").notNullable().defaultTo(false);
table.boolean("cache_bust").notNullable().defaultTo(false); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.dropColumn("cache_bust");
table.dropColumn("cache_bust"); });
});
}; };

View File

@ -1,8 +1,7 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.text("conditions").notNullable().defaultTo("[]");
table.text("conditions").notNullable().defaultTo("[]"); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -4,7 +4,6 @@ exports.up = function (knex) {
table.string("rabbitmq_username"); table.string("rabbitmq_username");
table.string("rabbitmq_password"); table.string("rabbitmq_password");
}); });
}; };
exports.down = function (knex) { exports.down = function (knex) {
@ -13,5 +12,4 @@ exports.down = function (knex) {
table.dropColumn("rabbitmq_username"); table.dropColumn("rabbitmq_username");
table.dropColumn("rabbitmq_password"); table.dropColumn("rabbitmq_password");
}); });
}; };

View File

@ -1,9 +1,8 @@
// Update info_json column to LONGTEXT mainly for MariaDB // Update info_json column to LONGTEXT mainly for MariaDB
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor_tls_info", function (table) {
.alterTable("monitor_tls_info", function (table) { table.text("info_json", "longtext").alter();
table.text("info_json", "longtext").alter(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,8 +1,7 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("smtp_security").defaultTo(null);
table.string("smtp_security").defaultTo(null); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,10 +1,9 @@
// Add websocket ignore headers and websocket subprotocol // Add websocket ignore headers and websocket subprotocol
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.boolean("ws_ignore_sec_websocket_accept_header").notNullable().defaultTo(false);
table.boolean("ws_ignore_sec_websocket_accept_header").notNullable().defaultTo(false); table.string("ws_subprotocol", 255).notNullable().defaultTo("");
table.string("ws_subprotocol", 255).notNullable().defaultTo(""); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -4,12 +4,12 @@ exports.up = function (knex) {
.alterTable("status_page", function (table) { .alterTable("status_page", function (table) {
table.renameColumn("google_analytics_tag_id", "analytics_id"); table.renameColumn("google_analytics_tag_id", "analytics_id");
table.string("analytics_script_url"); table.string("analytics_script_url");
table.enu("analytics_type", [ "google", "umami", "plausible", "matomo" ]).defaultTo(null); table.enu("analytics_type", ["google", "umami", "plausible", "matomo"]).defaultTo(null);
})
}).then(() => { .then(() => {
// After a succesful migration, add google as default for previous pages // After a succesful migration, add google as default for previous pages
knex("status_page").whereNotNull("analytics_id").update({ knex("status_page").whereNotNull("analytics_id").update({
"analytics_type": "google", analytics_type: "google",
}); });
}); });
}; };

View File

@ -5,20 +5,17 @@ ALTER TABLE monitor ADD ping_per_request_timeout INTEGER default 2 not null;
*/ */
exports.up = function (knex) { exports.up = function (knex) {
// Add new columns to table monitor // Add new columns to table monitor
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.integer("ping_count").defaultTo(1).notNullable();
table.integer("ping_count").defaultTo(1).notNullable(); table.boolean("ping_numeric").defaultTo(true).notNullable();
table.boolean("ping_numeric").defaultTo(true).notNullable(); table.integer("ping_per_request_timeout").defaultTo(2).notNullable();
table.integer("ping_per_request_timeout").defaultTo(2).notNullable(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.dropColumn("ping_count");
table.dropColumn("ping_count"); table.dropColumn("ping_numeric");
table.dropColumn("ping_numeric"); table.dropColumn("ping_per_request_timeout");
table.dropColumn("ping_per_request_timeout"); });
});
}; };

View File

@ -1,9 +1,8 @@
// Fix #5721: Change proxy port column type to integer to support larger port numbers // Fix #5721: Change proxy port column type to integer to support larger port numbers
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("proxy", function (table) {
.alterTable("proxy", function (table) { table.integer("port").alter();
table.integer("port").alter(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,9 +1,8 @@
// Add column custom_url to monitor_group table // Add column custom_url to monitor_group table
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor_group", function (table) {
.alterTable("monitor_group", function (table) { table.text("custom_url", "text");
table.text("custom_url", "text"); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,13 +1,11 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.boolean("ip_family").defaultTo(null);
table.boolean("ip_family").defaultTo(null); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.dropColumn("ip_family");
table.dropColumn("ip_family"); });
});
}; };

View File

@ -1,8 +1,7 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("manual_status").defaultTo(null);
table.string("manual_status").defaultTo(null); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,28 +1,27 @@
// Add column last_start_date to maintenance table // Add column last_start_date to maintenance table
exports.up = async function (knex) { exports.up = async function (knex) {
await knex.schema await knex.schema.alterTable("maintenance", function (table) {
.alterTable("maintenance", function (table) { table.datetime("last_start_date");
table.datetime("last_start_date"); });
});
// Perform migration for recurring-interval strategy // Perform migration for recurring-interval strategy
const recurringMaintenances = await knex("maintenance").where({ const recurringMaintenances = await knex("maintenance")
strategy: "recurring-interval", .where({
cron: "* * * * *" strategy: "recurring-interval",
}).select("id", "start_time"); cron: "* * * * *",
})
.select("id", "start_time");
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
const maintenanceUpdates = recurringMaintenances.map(async ({ start_time, id }) => { const maintenanceUpdates = recurringMaintenances.map(async ({ start_time, id }) => {
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
const [ hourStr, minuteStr ] = start_time.split(":"); const [hourStr, minuteStr] = start_time.split(":");
const hour = parseInt(hourStr, 10); const hour = parseInt(hourStr, 10);
const minute = parseInt(minuteStr, 10); const minute = parseInt(minuteStr, 10);
const cron = `${minute} ${hour} * * *`; const cron = `${minute} ${hour} * * *`;
await knex("maintenance") await knex("maintenance").where({ id }).update({ cron });
.where({ id })
.update({ cron });
}); });
await Promise.all(maintenanceUpdates); await Promise.all(maintenanceUpdates);
}; };

View File

@ -1,9 +1,8 @@
// Fix: Change manual_status column type to smallint // Fix: Change manual_status column type to smallint
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.smallint("manual_status").alter();
table.smallint("manual_status").alter(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,8 +1,7 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("oauth_audience").nullable().defaultTo(null);
table.string("oauth_audience").nullable().defaultTo(null); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {

View File

@ -1,15 +1,13 @@
exports.up = function (knex) { exports.up = function (knex) {
// Add new column monitor.mqtt_websocket_path // Add new column monitor.mqtt_websocket_path
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("mqtt_websocket_path", 255).nullable();
table.string("mqtt_websocket_path", 255).nullable(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
// Drop column monitor.mqtt_websocket_path // Drop column monitor.mqtt_websocket_path
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.dropColumn("mqtt_websocket_path");
table.dropColumn("mqtt_websocket_path"); });
});
}; };

View File

@ -1,16 +1,14 @@
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { // Fix ip_family, change to varchar instead of boolean
// Fix ip_family, change to varchar instead of boolean // possible values are "ipv4" and "ipv6"
// possible values are "ipv4" and "ipv6" table.string("ip_family", 4).defaultTo(null).alter();
table.string("ip_family", 4).defaultTo(null).alter(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { // Rollback to boolean
// Rollback to boolean table.boolean("ip_family").defaultTo(null).alter();
table.boolean("ip_family").defaultTo(null).alter(); });
});
}; };

View File

@ -1,15 +1,13 @@
exports.up = function (knex) { exports.up = function (knex) {
// Add new column status_page.show_only_last_heartbeat // Add new column status_page.show_only_last_heartbeat
return knex.schema return knex.schema.alterTable("status_page", function (table) {
.alterTable("status_page", function (table) { table.boolean("show_only_last_heartbeat").notNullable().defaultTo(false);
table.boolean("show_only_last_heartbeat").notNullable().defaultTo(false); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
// Drop column status_page.show_only_last_heartbeat // Drop column status_page.show_only_last_heartbeat
return knex.schema return knex.schema.alterTable("status_page", function (table) {
.alterTable("status_page", function (table) { table.dropColumn("show_only_last_heartbeat");
table.dropColumn("show_only_last_heartbeat"); });
});
}; };

View File

@ -9,11 +9,11 @@ exports.up = async function (knex) {
// Create partial indexes with predicate // Create partial indexes with predicate
await knex.schema.alterTable("heartbeat", function (table) { await knex.schema.alterTable("heartbeat", function (table) {
table.index([ "monitor_id", "time" ], "monitor_important_time_index", { table.index(["monitor_id", "time"], "monitor_important_time_index", {
predicate: knex.whereRaw("important = 1") predicate: knex.whereRaw("important = 1"),
}); });
table.index([ "important" ], "heartbeat_important_index", { table.index(["important"], "heartbeat_important_index", {
predicate: knex.whereRaw("important = 1") predicate: knex.whereRaw("important = 1"),
}); });
}); });
} }
@ -29,8 +29,8 @@ exports.down = async function (knex) {
await knex.raw("DROP INDEX IF EXISTS heartbeat_important_index"); await knex.raw("DROP INDEX IF EXISTS heartbeat_important_index");
await knex.schema.alterTable("heartbeat", function (table) { await knex.schema.alterTable("heartbeat", function (table) {
table.index([ "monitor_id", "important", "time" ], "monitor_important_time_index"); table.index(["monitor_id", "important", "time"], "monitor_important_time_index");
table.index([ "important" ]); table.index(["important"]);
}); });
} }
// For MariaDB/MySQL: No changes // For MariaDB/MySQL: No changes

View File

@ -1,14 +1,12 @@
// Change dns_last_result column from VARCHAR(255) to TEXT to handle longer DNS TXT records // Change dns_last_result column from VARCHAR(255) to TEXT to handle longer DNS TXT records
exports.up = function (knex) { exports.up = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.text("dns_last_result").alter();
table.text("dns_last_result").alter(); });
});
}; };
exports.down = function (knex) { exports.down = function (knex) {
return knex.schema return knex.schema.alterTable("monitor", function (table) {
.alterTable("monitor", function (table) { table.string("dns_last_result", 255).alter();
table.string("dns_last_result", 255).alter(); });
});
}; };

View File

@ -3,139 +3,139 @@
// Lookup table mapping v4 game IDs to v5 game IDs // Lookup table mapping v4 game IDs to v5 game IDs
const gameDig4to5IdMap = { const gameDig4to5IdMap = {
"americasarmypg": "aapg", americasarmypg: "aapg",
"7d2d": "sdtd", "7d2d": "sdtd",
"as": "actionsource", as: "actionsource",
"ageofchivalry": "aoc", ageofchivalry: "aoc",
"arkse": "ase", arkse: "ase",
"arcasimracing": "asr08", arcasimracing: "asr08",
"arma": "aaa", arma: "aaa",
"arma2oa": "a2oa", arma2oa: "a2oa",
"armacwa": "acwa", armacwa: "acwa",
"armar": "armaresistance", armar: "armaresistance",
"armare": "armareforger", armare: "armareforger",
"armagetron": "armagetronadvanced", armagetron: "armagetronadvanced",
"bat1944": "battalion1944", bat1944: "battalion1944",
"bf1942": "battlefield1942", bf1942: "battlefield1942",
"bfv": "battlefieldvietnam", bfv: "battlefieldvietnam",
"bf2": "battlefield2", bf2: "battlefield2",
"bf2142": "battlefield2142", bf2142: "battlefield2142",
"bfbc2": "bbc2", bfbc2: "bbc2",
"bf3": "battlefield3", bf3: "battlefield3",
"bf4": "battlefield4", bf4: "battlefield4",
"bfh": "battlefieldhardline", bfh: "battlefieldhardline",
"bd": "basedefense", bd: "basedefense",
"bs": "bladesymphony", bs: "bladesymphony",
"buildandshoot": "bas", buildandshoot: "bas",
"cod4": "cod4mw", cod4: "cod4mw",
"callofjuarez": "coj", callofjuarez: "coj",
"chivalry": "cmw", chivalry: "cmw",
"commandos3": "c3db", commandos3: "c3db",
"cacrenegade": "cacr", cacrenegade: "cacr",
"contactjack": "contractjack", contactjack: "contractjack",
"cs15": "counterstrike15", cs15: "counterstrike15",
"cs16": "counterstrike16", cs16: "counterstrike16",
"cs2": "counterstrike2", cs2: "counterstrike2",
"crossracing": "crce", crossracing: "crce",
"darkesthour": "dhe4445", darkesthour: "dhe4445",
"daysofwar": "dow", daysofwar: "dow",
"deadlydozenpt": "ddpt", deadlydozenpt: "ddpt",
"dh2005": "deerhunter2005", dh2005: "deerhunter2005",
"dinodday": "ddd", dinodday: "ddd",
"dirttrackracing2": "dtr2", dirttrackracing2: "dtr2",
"dmc": "deathmatchclassic", dmc: "deathmatchclassic",
"dnl": "dal", dnl: "dal",
"drakan": "dootf", drakan: "dootf",
"dys": "dystopia", dys: "dystopia",
"em": "empiresmod", em: "empiresmod",
"empyrion": "egs", empyrion: "egs",
"f12002": "formulaone2002", f12002: "formulaone2002",
"flashpointresistance": "ofr", flashpointresistance: "ofr",
"fivem": "gta5f", fivem: "gta5f",
"forrest": "theforrest", forrest: "theforrest",
"graw": "tcgraw", graw: "tcgraw",
"graw2": "tcgraw2", graw2: "tcgraw2",
"giantscitizenkabuto": "gck", giantscitizenkabuto: "gck",
"ges": "goldeneyesource", ges: "goldeneyesource",
"gore": "gus", gore: "gus",
"hldm": "hld", hldm: "hld",
"hldms": "hlds", hldms: "hlds",
"hlopfor": "hlof", hlopfor: "hlof",
"hl2dm": "hl2d", hl2dm: "hl2d",
"hidden": "thehidden", hidden: "thehidden",
"had2": "hiddendangerous2", had2: "hiddendangerous2",
"igi2": "i2cs", igi2: "i2cs",
"il2": "il2sturmovik", il2: "il2sturmovik",
"insurgencymic": "imic", insurgencymic: "imic",
"isle": "theisle", isle: "theisle",
"jamesbondnightfire": "jb007n", jamesbondnightfire: "jb007n",
"jc2mp": "jc2m", jc2mp: "jc2m",
"jc3mp": "jc3m", jc3mp: "jc3m",
"kingpin": "kloc", kingpin: "kloc",
"kisspc": "kpctnc", kisspc: "kpctnc",
"kspdmp": "kspd", kspdmp: "kspd",
"kzmod": "kreedzclimbing", kzmod: "kreedzclimbing",
"left4dead": "l4d", left4dead: "l4d",
"left4dead2": "l4d2", left4dead2: "l4d2",
"m2mp": "m2m", m2mp: "m2m",
"mohsh": "mohaas", mohsh: "mohaas",
"mohbt": "mohaab", mohbt: "mohaab",
"mohab": "moha", mohab: "moha",
"moh2010": "moh", moh2010: "moh",
"mohwf": "mohw", mohwf: "mohw",
"minecraftbe": "mbe", minecraftbe: "mbe",
"mtavc": "gtavcmta", mtavc: "gtavcmta",
"mtasa": "gtasamta", mtasa: "gtasamta",
"ns": "naturalselection", ns: "naturalselection",
"ns2": "naturalselection2", ns2: "naturalselection2",
"nwn": "neverwinternights", nwn: "neverwinternights",
"nwn2": "neverwinternights2", nwn2: "neverwinternights2",
"nolf": "tonolf", nolf: "tonolf",
"nolf2": "nolf2asihw", nolf2: "nolf2asihw",
"pvkii": "pvak2", pvkii: "pvak2",
"ps": "postscriptum", ps: "postscriptum",
"primalcarnage": "pce", primalcarnage: "pce",
"pc": "projectcars", pc: "projectcars",
"pc2": "projectcars2", pc2: "projectcars2",
"prbf2": "prb2", prbf2: "prb2",
"przomboid": "projectzomboid", przomboid: "projectzomboid",
"quake1": "quake", quake1: "quake",
"quake3": "q3a", quake3: "q3a",
"ragdollkungfu": "rdkf", ragdollkungfu: "rdkf",
"r6": "rainbowsix", r6: "rainbowsix",
"r6roguespear": "rs2rs", r6roguespear: "rs2rs",
"r6ravenshield": "rs3rs", r6ravenshield: "rs3rs",
"redorchestraost": "roo4145", redorchestraost: "roo4145",
"redm": "rdr2r", redm: "rdr2r",
"riseofnations": "ron", riseofnations: "ron",
"rs2": "rs2v", rs2: "rs2v",
"samp": "gtasam", samp: "gtasam",
"saomp": "gtasao", saomp: "gtasao",
"savage2": "s2ats", savage2: "s2ats",
"ss": "serioussam", ss: "serioussam",
"ss2": "serioussam2", ss2: "serioussam2",
"ship": "theship", ship: "theship",
"sinep": "sinepisodes", sinep: "sinepisodes",
"sonsoftheforest": "sotf", sonsoftheforest: "sotf",
"swbf": "swb", swbf: "swb",
"swbf2": "swb2", swbf2: "swb2",
"swjk": "swjkja", swjk: "swjkja",
"swjk2": "swjk2jo", swjk2: "swjk2jo",
"takeonhelicopters": "toh", takeonhelicopters: "toh",
"tf2": "teamfortress2", tf2: "teamfortress2",
"terraria": "terrariatshock", terraria: "terrariatshock",
"tribes1": "t1s", tribes1: "t1s",
"ut": "unrealtournament", ut: "unrealtournament",
"ut2003": "unrealtournament2003", ut2003: "unrealtournament2003",
"ut2004": "unrealtournament2004", ut2004: "unrealtournament2004",
"ut3": "unrealtournament3", ut3: "unrealtournament3",
"v8supercar": "v8sc", v8supercar: "v8sc",
"vcmp": "vcm", vcmp: "vcm",
"vs": "vampireslayer", vs: "vampireslayer",
"wheeloftime": "wot", wheeloftime: "wot",
"wolfenstein2009": "wolfenstein", wolfenstein2009: "wolfenstein",
"wolfensteinet": "wet", wolfensteinet: "wet",
"wurm": "wurmunlimited", wurm: "wurmunlimited",
}; };
/** /**
@ -146,10 +146,7 @@ const gameDig4to5IdMap = {
exports.up = async function (knex) { exports.up = async function (knex) {
await knex.transaction(async (trx) => { await knex.transaction(async (trx) => {
// Get all monitors that use the gamedig type // Get all monitors that use the gamedig type
const monitors = await trx("monitor") const monitors = await trx("monitor").select("id", "game").where("type", "gamedig").whereNotNull("game");
.select("id", "game")
.where("type", "gamedig")
.whereNotNull("game");
// Update each monitor with the new game ID if it needs migration // Update each monitor with the new game ID if it needs migration
for (const monitor of monitors) { for (const monitor of monitors) {
@ -157,9 +154,7 @@ exports.up = async function (knex) {
const newGameId = gameDig4to5IdMap[oldGameId]; const newGameId = gameDig4to5IdMap[oldGameId];
if (newGameId) { if (newGameId) {
await trx("monitor") await trx("monitor").where("id", monitor.id).update({ game: newGameId });
.where("id", monitor.id)
.update({ game: newGameId });
} }
} }
}); });
@ -172,16 +167,11 @@ exports.up = async function (knex) {
*/ */
exports.down = async function (knex) { exports.down = async function (knex) {
// Create reverse mapping from the same LUT // Create reverse mapping from the same LUT
const gameDig5to4IdMap = Object.fromEntries( const gameDig5to4IdMap = Object.fromEntries(Object.entries(gameDig4to5IdMap).map(([v4, v5]) => [v5, v4]));
Object.entries(gameDig4to5IdMap).map(([ v4, v5 ]) => [ v5, v4 ])
);
await knex.transaction(async (trx) => { await knex.transaction(async (trx) => {
// Get all monitors that use the gamedig type // Get all monitors that use the gamedig type
const monitors = await trx("monitor") const monitors = await trx("monitor").select("id", "game").where("type", "gamedig").whereNotNull("game");
.select("id", "game")
.where("type", "gamedig")
.whereNotNull("game");
// Revert each monitor back to the old game ID if it was migrated // Revert each monitor back to the old game ID if it was migrated
for (const monitor of monitors) { for (const monitor of monitors) {
@ -189,9 +179,7 @@ exports.down = async function (knex) {
const oldGameId = gameDig5to4IdMap[newGameId]; const oldGameId = gameDig5to4IdMap[newGameId];
if (oldGameId) { if (oldGameId) {
await trx("monitor") await trx("monitor").where("id", monitor.id).update({ game: oldGameId });
.where("id", monitor.id)
.update({ game: oldGameId });
} }
} }
}); });

View File

@ -11,13 +11,9 @@ https://knexjs.org/guide/migrations.html#knexfile-in-other-languages
## Template ## Template
```js ```js
exports.up = function(knex) { exports.up = function (knex) {};
}; exports.down = function (knex) {};
exports.down = function(knex) {
};
// exports.config = { transaction: false }; // exports.config = { transaction: false };
``` ```
@ -27,29 +23,28 @@ exports.down = function(knex) {
Filename: 2023-06-30-1348-create-user-and-product.js Filename: 2023-06-30-1348-create-user-and-product.js
```js ```js
exports.up = function(knex) { exports.up = function (knex) {
return knex.schema return knex.schema
.createTable('user', function (table) { .createTable("user", function (table) {
table.increments('id'); table.increments("id");
table.string('first_name', 255).notNullable(); table.string("first_name", 255).notNullable();
table.string('last_name', 255).notNullable(); table.string("last_name", 255).notNullable();
}) })
.createTable('product', function (table) { .createTable("product", function (table) {
table.increments('id'); table.increments("id");
table.decimal('price').notNullable(); table.decimal("price").notNullable();
table.string('name', 1000).notNullable(); table.string("name", 1000).notNullable();
}).then(() => { })
knex("products").insert([ .then(() => {
{ price: 10, name: "Apple" }, knex("products").insert([
{ price: 20, name: "Orange" }, { price: 10, name: "Apple" },
]); { price: 20, name: "Orange" },
]);
}); });
}; };
exports.down = function(knex) { exports.down = function (knex) {
return knex.schema return knex.schema.dropTable("product").dropTable("user");
.dropTable("product")
.dropTable("user");
}; };
``` ```

View File

@ -1,4 +1,4 @@
version: '3.8' version: "3.8"
services: services:
uptime-kuma: uptime-kuma:
@ -9,6 +9,5 @@ services:
- ../server:/app/server - ../server:/app/server
- ../db:/app/db - ../db:/app/db
ports: ports:
- "3001:3001" # <Host Port>:<Container Port> - "3001:3001" # <Host Port>:<Container Port>
- "3307:3306" - "3307:3306"

View File

@ -1,6 +1,8 @@
module.exports = { module.exports = {
apps: [{ apps: [
name: "uptime-kuma", {
script: "./server/server.js", name: "uptime-kuma",
}] script: "./server/server.js",
},
],
}; };

View File

@ -16,27 +16,26 @@ if (!version || !version.includes("-beta.")) {
const exists = tagExists(version); const exists = tagExists(version);
if (! exists) { if (!exists) {
// Process package.json // Process package.json
pkg.version = version; pkg.version = version;
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n"); fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
// Also update package-lock.json // Also update package-lock.json
const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm";
const resultVersion = childProcess.spawnSync(npm, [ "--no-git-tag-version", "version", version ], { shell: true }); const resultVersion = childProcess.spawnSync(npm, ["--no-git-tag-version", "version", version], { shell: true });
if (resultVersion.error) { if (resultVersion.error) {
console.error(resultVersion.error); console.error(resultVersion.error);
console.error("error npm version!"); console.error("error npm version!");
process.exit(1); process.exit(1);
} }
const resultInstall = childProcess.spawnSync(npm, [ "install" ], { shell: true }); const resultInstall = childProcess.spawnSync(npm, ["install"], { shell: true });
if (resultInstall.error) { if (resultInstall.error) {
console.error(resultInstall.error); console.error(resultInstall.error);
console.error("error update package-lock!"); console.error("error update package-lock!");
process.exit(1); process.exit(1);
} }
commit(version); commit(version);
} else { } else {
console.log("version tag exists, please delete the tag or use another tag"); console.log("version tag exists, please delete the tag or use another tag");
process.exit(1); process.exit(1);
@ -51,7 +50,7 @@ if (! exists) {
function commit(version) { function commit(version) {
let msg = "Update to " + version; let msg = "Update to " + version;
let res = childProcess.spawnSync("git", [ "commit", "-m", msg, "-a" ]); let res = childProcess.spawnSync("git", ["commit", "-m", msg, "-a"]);
let stdout = res.stdout.toString().trim(); let stdout = res.stdout.toString().trim();
console.log(stdout); console.log(stdout);
@ -59,7 +58,7 @@ function commit(version) {
throw new Error("commit error"); throw new Error("commit error");
} }
res = childProcess.spawnSync("git", [ "push", "origin", "master" ]); res = childProcess.spawnSync("git", ["push", "origin", "master"]);
console.log(res.stdout.toString().trim()); console.log(res.stdout.toString().trim());
} }
@ -70,11 +69,11 @@ function commit(version) {
* @throws Version is not valid * @throws Version is not valid
*/ */
function tagExists(version) { function tagExists(version) {
if (! version) { if (!version) {
throw new Error("invalid version"); throw new Error("invalid version");
} }
let res = childProcess.spawnSync("git", [ "tag", "-l", version ]); let res = childProcess.spawnSync("git", ["tag", "-l", version]);
return res.stdout.toString().trim() === version; return res.stdout.toString().trim() === version;
} }

View File

@ -14,7 +14,9 @@ if (platform === "linux/arm/v7") {
console.log("Already built in the host, skip."); console.log("Already built in the host, skip.");
process.exit(0); process.exit(0);
} else { } else {
console.log("prebuilt not found, it will be slow! You should execute `npm run build-healthcheck-armv7` before build."); console.log(
"prebuilt not found, it will be slow! You should execute `npm run build-healthcheck-armv7` before build."
);
} }
} else { } else {
if (fs.existsSync("./extra/healthcheck-armv7")) { if (fs.existsSync("./extra/healthcheck-armv7")) {
@ -24,4 +26,3 @@ if (platform === "linux/arm/v7") {
const output = childProcess.execSync("go build -x -o ./extra/healthcheck ./extra/healthcheck.go").toString("utf8"); const output = childProcess.execSync("go build -x -o ./extra/healthcheck ./extra/healthcheck.go").toString("utf8");
console.log(output); console.log(output);

View File

@ -18,7 +18,7 @@ const github = require("@actions/github");
await client.issues.listLabelsOnIssue({ await client.issues.listLabelsOnIssue({
owner: issue.owner, owner: issue.owner,
repo: issue.repo, repo: issue.repo,
issue_number: issue.number issue_number: issue.number,
}) })
).data.map(({ name }) => name); ).data.map(({ name }) => name);
@ -29,7 +29,7 @@ const github = require("@actions/github");
owner: issue.owner, owner: issue.owner,
repo: issue.repo, repo: issue.repo,
issue_number: issue.number, issue_number: issue.number,
labels: [ "invalid-format" ] labels: ["invalid-format"],
}); });
// Add the issue closing comment // Add the issue closing comment
@ -37,7 +37,7 @@ const github = require("@actions/github");
owner: issue.owner, owner: issue.owner,
repo: issue.repo, repo: issue.repo,
issue_number: issue.number, issue_number: issue.number,
body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please **DO NOT open blank issues and use our [issue-templates](https://github.com/louislam/uptime-kuma/issues/new/choose) instead**.\nBlank Issues do not contain the context necessary for a good discussions.` body: `@${username}: Hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. Please **DO NOT open blank issues and use our [issue-templates](https://github.com/louislam/uptime-kuma/issues/new/choose) instead**.\nBlank Issues do not contain the context necessary for a good discussions.`,
}); });
// Close the issue // Close the issue
@ -45,7 +45,7 @@ const github = require("@actions/github");
owner: issue.owner, owner: issue.owner,
repo: issue.repo, repo: issue.repo,
issue_number: issue.number, issue_number: issue.number,
state: "closed" state: "closed",
}); });
} else { } else {
console.log("Pass!"); console.log("Pass!");
@ -53,5 +53,4 @@ const github = require("@actions/github");
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
})(); })();

View File

@ -1,8 +1,7 @@
require("dotenv").config(); require("dotenv").config();
const { NodeSSH } = require("node-ssh"); const { NodeSSH } = require("node-ssh");
const readline = require("readline"); const readline = require("readline");
const rl = readline.createInterface({ input: process.stdin, const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
output: process.stdout });
const prompt = (query) => new Promise((resolve) => rl.question(query, resolve)); const prompt = (query) => new Promise((resolve) => rl.question(query, resolve));
(async () => { (async () => {
@ -13,7 +12,7 @@ const prompt = (query) => new Promise((resolve) => rl.question(query, resolve));
host: process.env.UPTIME_KUMA_DEMO_HOST, host: process.env.UPTIME_KUMA_DEMO_HOST,
port: process.env.UPTIME_KUMA_DEMO_PORT, port: process.env.UPTIME_KUMA_DEMO_PORT,
username: process.env.UPTIME_KUMA_DEMO_USERNAME, username: process.env.UPTIME_KUMA_DEMO_USERNAME,
privateKeyPath: process.env.UPTIME_KUMA_DEMO_PRIVATE_KEY_PATH privateKeyPath: process.env.UPTIME_KUMA_DEMO_PRIVATE_KEY_PATH,
}); });
let cwd = process.env.UPTIME_KUMA_DEMO_CWD; let cwd = process.env.UPTIME_KUMA_DEMO_CWD;
@ -48,7 +47,6 @@ const prompt = (query) => new Promise((resolve) => rl.question(query, resolve));
cwd, cwd,
}); });
console.log(result.stdout + result.stderr);*/ console.log(result.stdout + result.stderr);*/
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} finally { } finally {

View File

@ -26,7 +26,6 @@ function download(url) {
console.log("Extracting dist..."); console.log("Extracting dist...");
if (fs.existsSync("./dist")) { if (fs.existsSync("./dist")) {
if (fs.existsSync("./dist-backup")) { if (fs.existsSync("./dist-backup")) {
fs.rmSync("./dist-backup", { fs.rmSync("./dist-backup", {
recursive: true, recursive: true,

View File

@ -1,8 +1,8 @@
{ {
"name": "kuma-pr", "name": "kuma-pr",
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"bin": { "bin": {
"kuma-pr": "./index.mjs" "kuma-pr": "./index.mjs"
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"scripts": { "scripts": {
"start": "node index.js" "start": "node index.js"
} }
} }

View File

@ -1,6 +1,6 @@
// Supports: Deno, Bun, Node.js >= 18 (ts-node) // Supports: Deno, Bun, Node.js >= 18 (ts-node)
const pushURL : string = "https://example.com/api/push/key?status=up&msg=OK&ping="; const pushURL: string = "https://example.com/api/push/key?status=up&msg=OK&ping=";
const interval : number = 60; const interval: number = 60;
const push = async () => { const push = async () => {
await fetch(pushURL); await fetch(pushURL);

View File

@ -1,13 +1,13 @@
{ {
"scripts": { "scripts": {
"ts-node": "ts-node index.ts", "ts-node": "ts-node index.ts",
"deno": "deno run --allow-net index.ts", "deno": "deno run --allow-net index.ts",
"bun": "bun index.ts" "bun": "bun index.ts"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.6.0", "@types/node": "^20.6.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tslib": "^2.6.2", "tslib": "^2.6.2",
"typescript": "^5.2.2" "typescript": "^5.2.2"
} }
} }

View File

@ -8,7 +8,7 @@ async function main() {
const branch = process.argv[2]; const branch = process.argv[2];
// Use gh to get current branch's pr id // Use gh to get current branch's pr id
let currentBranchPRID = execSync("gh pr view --json number --jq \".number\"").toString().trim(); let currentBranchPRID = execSync('gh pr view --json number --jq ".number"').toString().trim();
console.log("Pr ID: ", currentBranchPRID); console.log("Pr ID: ", currentBranchPRID);
// Use gh commend to get pr commits // Use gh commend to get pr commits

View File

@ -8,7 +8,7 @@ const TwoFA = require("../server/2fa");
const args = require("args-parser")(process.argv); const args = require("args-parser")(process.argv);
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout,
}); });
const main = async () => { const main = async () => {
@ -19,7 +19,7 @@ const main = async () => {
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now. // No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
if (!process.env.TEST_BACKEND) { if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user"); const user = await R.findOne("user");
if (! user) { if (!user) {
throw new Error("user not found, have you installed?"); throw new Error("user not found, have you installed?");
} }
@ -31,7 +31,6 @@ const main = async () => {
await TwoFA.disable2FA(user.id); await TwoFA.disable2FA(user.id);
console.log("2FA has been removed successfully."); console.log("2FA has been removed successfully.");
} }
} }
} catch (e) { } catch (e) {
console.error("Error: " + e.message); console.error("Error: " + e.message);

View File

@ -21,4 +21,3 @@ const main = async () => {
}; };
main(); main();

View File

@ -12,7 +12,7 @@ const args = require("args-parser")(process.argv);
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout,
}); });
const main = async () => { const main = async () => {
@ -28,7 +28,7 @@ const main = async () => {
// No need to actually reset the password for testing, just make sure no connection problem. It is ok for now. // No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
if (!process.env.TEST_BACKEND) { if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user"); const user = await R.findOne("user");
if (! user) { if (!user) {
throw new Error("user not found, have you installed?"); throw new Error("user not found, have you installed?");
} }
@ -41,7 +41,10 @@ const main = async () => {
// When called with "--new-password" argument for unattended modification (e.g. npm run reset-password -- --new_password=secret) // When called with "--new-password" argument for unattended modification (e.g. npm run reset-password -- --new_password=secret)
if ("new-password" in args) { if ("new-password" in args) {
console.log("Using password from argument"); console.log("Using password from argument");
console.warn("\x1b[31m%s\x1b[0m", "Warning: the password might be stored, in plain text, in your shell's history"); console.warn(
"\x1b[31m%s\x1b[0m",
"Warning: the password might be stored, in plain text, in your shell's history"
);
password = confirmPassword = args["new-password"] + ""; password = confirmPassword = args["new-password"] + "";
if (passwordStrength(password).value === "Too weak") { if (passwordStrength(password).value === "Too weak") {
throw new Error("Password is too weak, please use a stronger password."); throw new Error("Password is too weak, please use a stronger password.");
@ -71,7 +74,6 @@ const main = async () => {
} }
} }
console.log("Password reset successfully."); console.log("Password reset successfully.");
} }
} catch (e) { } catch (e) {
console.error("Error: " + e.message); console.error("Error: " + e.message);
@ -112,19 +114,23 @@ function disconnectAllSocketClients(username, password) {
timeout: 5000, timeout: 5000,
}); });
socket.on("connect", () => { socket.on("connect", () => {
socket.emit("login", { socket.emit(
username, "login",
password, {
}, (res) => { username,
if (res.ok) { password,
console.log("Logged in."); },
socket.emit("disconnectOtherSocketClients"); (res) => {
} else { if (res.ok) {
console.warn("Login failed."); console.log("Logged in.");
console.warn("Please restart the server to disconnect all sessions."); socket.emit("disconnectOtherSocketClients");
} else {
console.warn("Login failed.");
console.warn("Please restart the server to disconnect all sessions.");
}
socket.close();
} }
socket.close(); );
});
}); });
socket.on("connect_error", function () { socket.on("connect_error", function () {

View File

@ -7,7 +7,7 @@ const dns2 = require("dns2");
const { Packet } = dns2; const { Packet } = dns2;
const server = dns2.createServer({ const server = dns2.createServer({
udp: true udp: true,
}); });
server.on("request", (request, send, rinfo) => { server.on("request", (request, send, rinfo) => {
@ -17,14 +17,13 @@ server.on("request", (request, send, rinfo) => {
const response = Packet.createResponseFromRequest(request); const response = Packet.createResponseFromRequest(request);
if (question.name === "existing.com") { if (question.name === "existing.com") {
if (question.type === Packet.TYPE.A) { if (question.type === Packet.TYPE.A) {
response.answers.push({ response.answers.push({
name: question.name, name: question.name,
type: question.type, type: question.type,
class: question.class, class: question.class,
ttl: 300, ttl: 300,
address: "1.2.3.4" address: "1.2.3.4",
}); });
} else if (question.type === Packet.TYPE.AAAA) { } else if (question.type === Packet.TYPE.AAAA) {
response.answers.push({ response.answers.push({
@ -49,7 +48,7 @@ server.on("request", (request, send, rinfo) => {
class: question.class, class: question.class,
ttl: 300, ttl: 300,
exchange: "mx1.existing.com", exchange: "mx1.existing.com",
priority: 5 priority: 5,
}); });
} else if (question.type === Packet.TYPE.NS) { } else if (question.type === Packet.TYPE.NS) {
response.answers.push({ response.answers.push({
@ -103,7 +102,6 @@ server.on("request", (request, send, rinfo) => {
value: "ca.existing.com", value: "ca.existing.com",
}); });
} }
} }
if (question.name === "4.3.2.1.in-addr.arpa") { if (question.name === "4.3.2.1.in-addr.arpa") {
@ -132,7 +130,7 @@ server.on("close", () => {
}); });
server.listen({ server.listen({
udp: 5300 udp: 5300,
}); });
/** /**

View File

@ -41,17 +41,19 @@ server1.aedes.on("subscribe", (subscriptions, client) => {
for (let s of subscriptions) { for (let s of subscriptions) {
if (s.topic === "test") { if (s.topic === "test") {
server1.aedes.publish({ server1.aedes.publish(
topic: "test", {
payload: Buffer.from("ok"), topic: "test",
}, (error) => { payload: Buffer.from("ok"),
if (error) { },
log.error("mqtt_server", error); (error) => {
if (error) {
log.error("mqtt_server", error);
}
} }
}); );
} }
} }
}); });
server1.start(); server1.start();

View File

@ -10,7 +10,7 @@ let lines = file.split("\n");
lines = lines.filter((line) => line !== ""); lines = lines.filter((line) => line !== "");
// Remove duplicates // Remove duplicates
lines = [ ...new Set(lines) ]; lines = [...new Set(lines)];
// Remove @weblate and @UptimeKumaBot // Remove @weblate and @UptimeKumaBot
lines = lines.filter((line) => line !== "@weblate" && line !== "@UptimeKumaBot" && line !== "@louislam"); lines = lines.filter((line) => line !== "@weblate" && line !== "@UptimeKumaBot" && line !== "@louislam");

View File

@ -54,13 +54,13 @@ async function updateLanguage(langCode, baseLangCode) {
} else { } else {
console.log("Empty file"); console.log("Empty file");
obj = { obj = {
languageName: "<Your Language name in your language (not in English)>" languageName: "<Your Language name in your language (not in English)>",
}; };
} }
// En first // En first
for (const key in en) { for (const key in en) {
if (! obj[key]) { if (!obj[key]) {
obj[key] = en[key]; obj[key] = en[key];
} }
} }
@ -68,15 +68,17 @@ async function updateLanguage(langCode, baseLangCode) {
if (baseLang !== en) { if (baseLang !== en) {
// Base second // Base second
for (const key in baseLang) { for (const key in baseLang) {
if (! obj[key]) { if (!obj[key]) {
obj[key] = key; obj[key] = key;
} }
} }
} }
const code = "export default " + util.inspect(obj, { const code =
depth: null, "export default " +
}); util.inspect(obj, {
depth: null,
});
fs.writeFileSync(`../../src/languages/${file}`, code); fs.writeFileSync(`../../src/languages/${file}`, code);
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "update-language-files", "name": "update-language-files",
"type": "module", "type": "module",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
"license": "ISC" "license": "ISC"
} }

View File

@ -9,15 +9,14 @@ const newVersion = process.env.RELEASE_VERSION;
console.log("New Version: " + newVersion); console.log("New Version: " + newVersion);
if (! newVersion) { if (!newVersion) {
console.error("invalid version"); console.error("invalid version");
process.exit(1); process.exit(1);
} }
const exists = tagExists(newVersion); const exists = tagExists(newVersion);
if (! exists) { if (!exists) {
// Process package.json // Process package.json
pkg.version = newVersion; pkg.version = newVersion;
@ -27,20 +26,19 @@ if (! exists) {
// Also update package-lock.json // Also update package-lock.json
const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm";
const resultVersion = childProcess.spawnSync(npm, [ "--no-git-tag-version", "version", newVersion ], { shell: true }); const resultVersion = childProcess.spawnSync(npm, ["--no-git-tag-version", "version", newVersion], { shell: true });
if (resultVersion.error) { if (resultVersion.error) {
console.error(resultVersion.error); console.error(resultVersion.error);
console.error("error npm version!"); console.error("error npm version!");
process.exit(1); process.exit(1);
} }
const resultInstall = childProcess.spawnSync(npm, [ "install" ], { shell: true }); const resultInstall = childProcess.spawnSync(npm, ["install"], { shell: true });
if (resultInstall.error) { if (resultInstall.error) {
console.error(resultInstall.error); console.error(resultInstall.error);
console.error("error update package-lock!"); console.error("error update package-lock!");
process.exit(1); process.exit(1);
} }
commit(newVersion); commit(newVersion);
} else { } else {
console.log("version exists"); console.log("version exists");
} }
@ -54,7 +52,7 @@ if (! exists) {
function commit(version) { function commit(version) {
let msg = "Update to " + version; let msg = "Update to " + version;
let res = childProcess.spawnSync("git", [ "commit", "-m", msg, "-a" ]); let res = childProcess.spawnSync("git", ["commit", "-m", msg, "-a"]);
let stdout = res.stdout.toString().trim(); let stdout = res.stdout.toString().trim();
console.log(stdout); console.log(stdout);
@ -70,11 +68,11 @@ function commit(version) {
* @throws Version is not valid * @throws Version is not valid
*/ */
function tagExists(version) { function tagExists(version) {
if (! version) { if (!version) {
throw new Error("invalid version"); throw new Error("invalid version");
} }
let res = childProcess.spawnSync("git", [ "tag", "-l", version ]); let res = childProcess.spawnSync("git", ["tag", "-l", version]);
return res.stdout.toString().trim() === version; return res.stdout.toString().trim() === version;
} }

View File

@ -21,23 +21,23 @@ function updateWiki(newVersion) {
safeDelete(wikiDir); safeDelete(wikiDir);
childProcess.spawnSync("git", [ "clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir ]); childProcess.spawnSync("git", ["clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir]);
let content = fs.readFileSync(howToUpdateFilename).toString(); let content = fs.readFileSync(howToUpdateFilename).toString();
// Replace the version: https://regex101.com/r/hmj2Bc/1 // Replace the version: https://regex101.com/r/hmj2Bc/1
content = content.replace(/(git checkout )([^\s]+)/, `$1${newVersion}`); content = content.replace(/(git checkout )([^\s]+)/, `$1${newVersion}`);
fs.writeFileSync(howToUpdateFilename, content); fs.writeFileSync(howToUpdateFilename, content);
childProcess.spawnSync("git", [ "add", "-A" ], { childProcess.spawnSync("git", ["add", "-A"], {
cwd: wikiDir, cwd: wikiDir,
}); });
childProcess.spawnSync("git", [ "commit", "-m", `Update to ${newVersion}` ], { childProcess.spawnSync("git", ["commit", "-m", `Update to ${newVersion}`], {
cwd: wikiDir, cwd: wikiDir,
}); });
console.log("Pushing to Github"); console.log("Pushing to Github");
childProcess.spawnSync("git", [ "push" ], { childProcess.spawnSync("git", ["push"], {
cwd: wikiDir, cwd: wikiDir,
}); });

View File

@ -9,16 +9,16 @@ if (!platform) {
const supportedPlatforms = [ const supportedPlatforms = [
{ {
name: "linux/amd64", name: "linux/amd64",
bin: "./build/uptime-kuma-push-amd64" bin: "./build/uptime-kuma-push-amd64",
}, },
{ {
name: "linux/arm64", name: "linux/arm64",
bin: "./build/uptime-kuma-push-arm64" bin: "./build/uptime-kuma-push-arm64",
}, },
{ {
name: "linux/arm/v7", name: "linux/arm/v7",
bin: "./build/uptime-kuma-push-armv7" bin: "./build/uptime-kuma-push-armv7",
} },
]; ];
let platformObj = null; let platformObj = null;
@ -45,4 +45,3 @@ if (platformObj) {
console.error("Unsupported platform: " + platform); console.error("Unsupported platform: " + platform);
process.exit(1); process.exit(1);
} }

View File

@ -1,13 +1,13 @@
{ {
"scripts": { "scripts": {
"build-docker": "npm run build-all && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:push . --push --target release", "build-docker": "npm run build-all && docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:push . --push --target release",
"build-all": "npm run build-win && npm run build-linux-amd64 && npm run build-linux-arm64 && npm run build-linux-armv7 && npm run build-linux-armv6 && npm run build-linux-armv5 && npm run build-linux-riscv64", "build-all": "npm run build-win && npm run build-linux-amd64 && npm run build-linux-arm64 && npm run build-linux-armv7 && npm run build-linux-armv6 && npm run build-linux-armv5 && npm run build-linux-riscv64",
"build-win": "cross-env GOOS=windows GOARCH=amd64 go build -x -o ./build/uptime-kuma-push.exe uptime-kuma-push.go", "build-win": "cross-env GOOS=windows GOARCH=amd64 go build -x -o ./build/uptime-kuma-push.exe uptime-kuma-push.go",
"build-linux-amd64": "cross-env GOOS=linux GOARCH=amd64 go build -x -o ./build/uptime-kuma-push-amd64 uptime-kuma-push.go", "build-linux-amd64": "cross-env GOOS=linux GOARCH=amd64 go build -x -o ./build/uptime-kuma-push-amd64 uptime-kuma-push.go",
"build-linux-arm64": "cross-env GOOS=linux GOARCH=arm64 go build -x -o ./build/uptime-kuma-push-arm64 uptime-kuma-push.go", "build-linux-arm64": "cross-env GOOS=linux GOARCH=arm64 go build -x -o ./build/uptime-kuma-push-arm64 uptime-kuma-push.go",
"build-linux-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./build/uptime-kuma-push-armv7 uptime-kuma-push.go", "build-linux-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./build/uptime-kuma-push-armv7 uptime-kuma-push.go",
"build-linux-armv6": "cross-env GOOS=linux GOARCH=arm GOARM=6 go build -x -o ./build/uptime-kuma-push-armv6 uptime-kuma-push.go", "build-linux-armv6": "cross-env GOOS=linux GOARCH=arm GOARM=6 go build -x -o ./build/uptime-kuma-push-armv6 uptime-kuma-push.go",
"build-linux-armv5": "cross-env GOOS=linux GOARCH=arm GOARM=5 go build -x -o ./build/uptime-kuma-push-armv5 uptime-kuma-push.go", "build-linux-armv5": "cross-env GOOS=linux GOARCH=arm GOARM=5 go build -x -o ./build/uptime-kuma-push-armv5 uptime-kuma-push.go",
"build-linux-riscv64": "cross-env GOOS=linux GOARCH=riscv64 go build -x -o ./build/uptime-kuma-push-riscv64 uptime-kuma-push.go" "build-linux-riscv64": "cross-env GOOS=linux GOARCH=riscv64 go build -x -o ./build/uptime-kuma-push-riscv64 uptime-kuma-push.go"
} }
} }

40062
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,231 +1,231 @@
{ {
"name": "uptime-kuma", "name": "uptime-kuma",
"version": "2.1.0-beta.1", "version": "2.1.0-beta.1",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/louislam/uptime-kuma.git" "url": "https://github.com/louislam/uptime-kuma.git"
}, },
"engines": { "engines": {
"node": ">= 20.4.0" "node": ">= 20.4.0"
}, },
"scripts": { "scripts": {
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint:js-prod": "npm run lint:js -- --max-warnings 0", "lint:js-prod": "npm run lint:js -- --max-warnings 0",
"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",
"lint-fix:style": "stylelint \"**/*.{vue,css,scss}\" --fix --ignore-path .gitignore", "lint-fix:style": "stylelint \"**/*.{vue,css,scss}\" --fix --ignore-path .gitignore",
"lint": "npm run lint:js && npm run lint:style", "lint": "npm run lint:js && npm run lint:style",
"fmt": "prettier --write \"**/*.{js,ts,vue,css,scss,json,md,yml,yaml}\" --ignore-path .gitignore", "fmt": "prettier --write \"**/*.{js,ts,vue,css,scss,json,md,yml,yaml}\"",
"lint:prod": "npm run lint:js-prod && npm run lint:style", "lint:prod": "npm run lint:js-prod && npm run lint:style",
"dev": "concurrently -k -r \"wait-on tcp:3000 && npm run start-server-dev \" \"npm run start-frontend-dev\"", "dev": "concurrently -k -r \"wait-on tcp:3000 && npm run start-server-dev \" \"npm run start-frontend-dev\"",
"start-frontend-dev": "cross-env NODE_ENV=development vite --host --config ./config/vite.config.js", "start-frontend-dev": "cross-env NODE_ENV=development vite --host --config ./config/vite.config.js",
"start-frontend-devcontainer": "cross-env NODE_ENV=development DEVCONTAINER=1 vite --host --config ./config/vite.config.js", "start-frontend-devcontainer": "cross-env NODE_ENV=development DEVCONTAINER=1 vite --host --config ./config/vite.config.js",
"start": "npm run start-server", "start": "npm run start-server",
"start-server": "node server/server.js", "start-server": "node server/server.js",
"start-server-dev": "cross-env NODE_ENV=development node server/server.js", "start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"start-server-dev:watch": "cross-env NODE_ENV=development node --watch server/server.js", "start-server-dev:watch": "cross-env NODE_ENV=development node --watch server/server.js",
"build": "vite build --config ./config/vite.config.js", "build": "vite build --config ./config/vite.config.js",
"test": "npm run test-backend && npm run test-e2e", "test": "npm run test-backend && npm run test-e2e",
"test-with-build": "npm run build && npm test", "test-with-build": "npm run build && npm test",
"test-backend": "node test/test-backend.mjs", "test-backend": "node test/test-backend.mjs",
"test-backend-22": "cross-env TEST_BACKEND=1 node --test --test-reporter=spec \"test/backend-test/**/*.js\"", "test-backend-22": "cross-env TEST_BACKEND=1 node --test --test-reporter=spec \"test/backend-test/**/*.js\"",
"test-backend-20": "cross-env TEST_BACKEND=1 node --test --test-reporter=spec test/backend-test", "test-backend-20": "cross-env TEST_BACKEND=1 node --test --test-reporter=spec test/backend-test",
"test-e2e": "playwright test --config ./config/playwright.config.js", "test-e2e": "playwright test --config ./config/playwright.config.js",
"test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063", "test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063",
"playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json", "playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json",
"playwright-show-report": "playwright show-report ./private/playwright-report", "playwright-show-report": "playwright show-report ./private/playwright-report",
"tsc": "tsc --project ./tsconfig-backend.json", "tsc": "tsc --project ./tsconfig-backend.json",
"vite-preview-dist": "vite preview --host --config ./config/vite.config.js", "vite-preview-dist": "vite preview --host --config ./config/vite.config.js",
"build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push", "build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push",
"build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push", "build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push",
"build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push", "build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push",
"build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .", "build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .",
"build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --push",
"upload-artifacts": "node extra/release/upload-artifacts.mjs", "upload-artifacts": "node extra/release/upload-artifacts.mjs",
"upload-artifacts-beta": "node extra/release/upload-artifacts-beta.mjs", "upload-artifacts-beta": "node extra/release/upload-artifacts-beta.mjs",
"setup": "git checkout 2.0.2 && npm ci --omit dev --no-audit && npm run download-dist", "setup": "git checkout 2.0.2 && npm ci --omit dev --no-audit && npm run download-dist",
"download-dist": "node extra/download-dist.js", "download-dist": "node extra/download-dist.js",
"mark-as-nightly": "node extra/mark-as-nightly.js", "mark-as-nightly": "node extra/mark-as-nightly.js",
"reset-password": "node extra/reset-password.js", "reset-password": "node extra/reset-password.js",
"remove-2fa": "node extra/remove-2fa.js", "remove-2fa": "node extra/remove-2fa.js",
"simple-dns-server": "node extra/simple-dns-server.js", "simple-dns-server": "node extra/simple-dns-server.js",
"simple-mqtt-server": "node extra/simple-mqtt-server.js", "simple-mqtt-server": "node extra/simple-mqtt-server.js",
"simple-mongo": "docker run --rm -p 27017:27017 mongo", "simple-mongo": "docker run --rm -p 27017:27017 mongo",
"simple-postgres": "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres", "simple-postgres": "docker run --rm -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres",
"simple-mariadb": "docker run --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mariadb# mariadb", "simple-mariadb": "docker run --rm -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mariadb# mariadb",
"update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix", "update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix",
"release-final": "node ./extra/release/final.mjs", "release-final": "node ./extra/release/final.mjs",
"release-beta": "node ./extra/release/beta.mjs", "release-beta": "node ./extra/release/beta.mjs",
"release-nightly": "node ./extra/release/nightly.mjs", "release-nightly": "node ./extra/release/nightly.mjs",
"git-remove-tag": "git tag -d", "git-remove-tag": "git tag -d",
"build-dist-and-restart": "npm run build && npm run start-server-dev", "build-dist-and-restart": "npm run build && npm run start-server-dev",
"start-pr-test": "node extra/checkout-pr.mjs && npm install && npm run dev", "start-pr-test": "node extra/checkout-pr.mjs && npm install && npm run dev",
"build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go", "build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go",
"deploy-demo-server": "node extra/deploy-demo-server.js", "deploy-demo-server": "node extra/deploy-demo-server.js",
"sort-contributors": "node extra/sort-contributors.js", "sort-contributors": "node extra/sort-contributors.js",
"quick-run-nightly": "docker run --rm --env NODE_ENV=development -p 3001:3001 louislam/uptime-kuma:nightly2", "quick-run-nightly": "docker run --rm --env NODE_ENV=development -p 3001:3001 louislam/uptime-kuma:nightly2",
"start-dev-container": "cd docker && docker-compose -f docker-compose-dev.yml up --force-recreate", "start-dev-container": "cd docker && docker-compose -f docker-compose-dev.yml up --force-recreate",
"rebase-pr-to-1.23.X": "node extra/rebase-pr.js 1.23.X", "rebase-pr-to-1.23.X": "node extra/rebase-pr.js 1.23.X",
"reset-migrate-aggregate-table-state": "node extra/reset-migrate-aggregate-table-state.js", "reset-migrate-aggregate-table-state": "node extra/reset-migrate-aggregate-table-state.js",
"generate-changelog": "node ./extra/generate-changelog.mjs" "generate-changelog": "node ./extra/generate-changelog.mjs"
}, },
"dependencies": { "dependencies": {
"@grpc/grpc-js": "~1.8.22", "@grpc/grpc-js": "~1.8.22",
"@louislam/ping": "~0.4.4-mod.1", "@louislam/ping": "~0.4.4-mod.1",
"@louislam/sqlite3": "15.1.6", "@louislam/sqlite3": "15.1.6",
"@vvo/tzdb": "^6.125.0", "@vvo/tzdb": "^6.125.0",
"args-parser": "~1.3.0", "args-parser": "~1.3.0",
"axios": "~0.30.0", "axios": "~0.30.0",
"badge-maker": "~3.3.1", "badge-maker": "~3.3.1",
"bcryptjs": "~2.4.3", "bcryptjs": "~2.4.3",
"chardet": "~1.4.0", "chardet": "~1.4.0",
"check-password-strength": "^2.0.5", "check-password-strength": "^2.0.5",
"cheerio": "~1.0.0-rc.12", "cheerio": "~1.0.0-rc.12",
"chroma-js": "~2.4.2", "chroma-js": "~2.4.2",
"command-exists": "~1.2.9", "command-exists": "~1.2.9",
"compare-versions": "~3.6.0", "compare-versions": "~3.6.0",
"compression": "~1.8.1", "compression": "~1.8.1",
"country-flag-emoji-polyfill": "^0.1.8", "country-flag-emoji-polyfill": "^0.1.8",
"croner": "~8.1.0", "croner": "~8.1.0",
"dayjs": "~1.11.5", "dayjs": "~1.11.5",
"dev-null": "^0.1.1", "dev-null": "^0.1.1",
"dotenv": "~16.0.3", "dotenv": "~16.0.3",
"express": "~4.21.0", "express": "~4.21.0",
"express-basic-auth": "~1.2.1", "express-basic-auth": "~1.2.1",
"express-static-gzip": "~2.1.7", "express-static-gzip": "~2.1.7",
"feed": "^4.2.2", "feed": "^4.2.2",
"form-data": "~4.0.0", "form-data": "~4.0.0",
"gamedig": "^5.0.1", "gamedig": "^5.0.1",
"html-escaper": "^3.0.3", "html-escaper": "^3.0.3",
"http-cookie-agent": "~5.0.4", "http-cookie-agent": "~5.0.4",
"http-graceful-shutdown": "~3.1.7", "http-graceful-shutdown": "~3.1.7",
"http-proxy-agent": "~7.0.2", "http-proxy-agent": "~7.0.2",
"https-proxy-agent": "~7.0.6", "https-proxy-agent": "~7.0.6",
"iconv-lite": "~0.6.3", "iconv-lite": "~0.6.3",
"is-url": "^1.2.4", "is-url": "^1.2.4",
"isomorphic-ws": "^5.0.0", "isomorphic-ws": "^5.0.0",
"jsesc": "~3.0.2", "jsesc": "~3.0.2",
"jsonata": "^2.0.3", "jsonata": "^2.0.3",
"jsonwebtoken": "~9.0.0", "jsonwebtoken": "~9.0.0",
"jwt-decode": "~3.1.2", "jwt-decode": "~3.1.2",
"kafkajs": "^2.2.4", "kafkajs": "^2.2.4",
"knex": "~3.1.0", "knex": "~3.1.0",
"limiter": "~2.1.0", "limiter": "~2.1.0",
"liquidjs": "^10.7.0", "liquidjs": "^10.7.0",
"marked": "^14.0.0", "marked": "^14.0.0",
"mitt": "~3.0.1", "mitt": "~3.0.1",
"mongodb": "~4.17.1", "mongodb": "~4.17.1",
"mqtt": "~4.3.7", "mqtt": "~4.3.7",
"mssql": "~12.0.0", "mssql": "~12.0.0",
"mysql2": "~3.11.3", "mysql2": "~3.11.3",
"nanoid": "~3.3.4", "nanoid": "~3.3.4",
"net-snmp": "^3.11.2", "net-snmp": "^3.11.2",
"node-cloudflared-tunnel": "~1.0.9", "node-cloudflared-tunnel": "~1.0.9",
"node-fetch-cache": "^5.1.0", "node-fetch-cache": "^5.1.0",
"node-radius-utils": "~1.2.0", "node-radius-utils": "~1.2.0",
"nodemailer": "~7.0.12", "nodemailer": "~7.0.12",
"nostr-tools": "^2.10.4", "nostr-tools": "^2.10.4",
"notp": "~2.0.3", "notp": "~2.0.3",
"openid-client": "^5.4.2", "openid-client": "^5.4.2",
"password-hash": "~1.2.2", "password-hash": "~1.2.2",
"pg": "~8.11.3", "pg": "~8.11.3",
"pg-connection-string": "~2.6.2", "pg-connection-string": "~2.6.2",
"playwright-core": "~1.39.0", "playwright-core": "~1.39.0",
"prom-client": "~13.2.0", "prom-client": "~13.2.0",
"prometheus-api-metrics": "~3.2.1", "prometheus-api-metrics": "~3.2.1",
"promisify-child-process": "~4.1.2", "promisify-child-process": "~4.1.2",
"protobufjs": "~7.2.4", "protobufjs": "~7.2.4",
"qs": "~6.14.1", "qs": "~6.14.1",
"radius": "~1.1.4", "radius": "~1.1.4",
"redbean-node": "~0.3.0", "redbean-node": "~0.3.0",
"redis": "~5.9.0", "redis": "~5.9.0",
"semver": "~7.5.4", "semver": "~7.5.4",
"socket.io": "~4.8.0", "socket.io": "~4.8.0",
"socket.io-client": "~4.8.0", "socket.io-client": "~4.8.0",
"socks-proxy-agent": "~8.0.5", "socks-proxy-agent": "~8.0.5",
"sqlstring": "~2.3.3", "sqlstring": "~2.3.3",
"tar": "~6.2.1", "tar": "~6.2.1",
"tcp-ping": "~0.1.1", "tcp-ping": "~0.1.1",
"thirty-two": "~1.0.2", "thirty-two": "~1.0.2",
"tldts": "^7.0.19", "tldts": "^7.0.19",
"tough-cookie": "~4.1.3", "tough-cookie": "~4.1.3",
"validator": "^13.15.26", "validator": "^13.15.26",
"web-push": "^3.6.7", "web-push": "^3.6.7",
"ws": "^8.13.0" "ws": "^8.13.0"
}, },
"devDependencies": { "devDependencies": {
"@actions/github": "~6.0.0", "@actions/github": "~6.0.0",
"@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",
"@playwright/test": "~1.39.0", "@playwright/test": "~1.39.0",
"@popperjs/core": "~2.10.2", "@popperjs/core": "~2.10.2",
"@testcontainers/hivemq": "^10.13.1", "@testcontainers/hivemq": "^10.13.1",
"@testcontainers/mariadb": "^10.13.0", "@testcontainers/mariadb": "^10.13.0",
"@testcontainers/mssqlserver": "^10.28.0", "@testcontainers/mssqlserver": "^10.28.0",
"@testcontainers/mysql": "^11.11.0", "@testcontainers/mysql": "^11.11.0",
"@testcontainers/postgresql": "^11.9.0", "@testcontainers/postgresql": "^11.9.0",
"@testcontainers/rabbitmq": "^10.13.2", "@testcontainers/rabbitmq": "^10.13.2",
"@types/bootstrap": "~5.1.9", "@types/bootstrap": "~5.1.9",
"@types/node": "^20.8.6", "@types/node": "^20.8.6",
"@types/web-push": "^3.6.4", "@types/web-push": "^3.6.4",
"@typescript-eslint/eslint-plugin": "^6.7.5", "@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5", "@typescript-eslint/parser": "^6.7.5",
"@vitejs/plugin-vue": "~5.0.1", "@vitejs/plugin-vue": "~5.0.1",
"@vue/compiler-sfc": "~3.4.2", "@vue/compiler-sfc": "~3.4.2",
"@vuepic/vue-datepicker": "~3.4.8", "@vuepic/vue-datepicker": "~3.4.8",
"aedes": "^0.46.3", "aedes": "^0.46.3",
"bootstrap": "5.1.3", "bootstrap": "5.1.3",
"chart.js": "~4.2.1", "chart.js": "~4.2.1",
"chartjs-adapter-dayjs-4": "~1.0.4", "chartjs-adapter-dayjs-4": "~1.0.4",
"concurrently": "^7.1.0", "concurrently": "^7.1.0",
"core-js": "~3.26.1", "core-js": "~3.26.1",
"cronstrue": "~2.24.0", "cronstrue": "~2.24.0",
"cross-env": "~7.0.3", "cross-env": "~7.0.3",
"delay": "^5.0.0", "delay": "^5.0.0",
"dns2": "~2.0.1", "dns2": "~2.0.1",
"dompurify": "~3.2.4", "dompurify": "~3.2.4",
"eslint": "~8.14.0", "eslint": "~8.14.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-jsdoc": "~46.4.6", "eslint-plugin-jsdoc": "~46.4.6",
"eslint-plugin-vue": "~8.7.1", "eslint-plugin-vue": "~8.7.1",
"favico.js": "~0.3.10", "favico.js": "~0.3.10",
"get-port-please": "^3.1.1", "get-port-please": "^3.1.1",
"node-ssh": "~13.1.0", "node-ssh": "~13.1.0",
"postcss-html": "~1.5.0", "postcss-html": "~1.5.0",
"postcss-rtlcss": "~3.7.2", "postcss-rtlcss": "~3.7.2",
"postcss-scss": "~4.0.4", "postcss-scss": "~4.0.4",
"prettier": "^3.7.4", "prettier": "^3.7.4",
"prismjs": "~1.30.0", "prismjs": "~1.30.0",
"qrcode": "~1.5.0", "qrcode": "~1.5.0",
"rollup-plugin-visualizer": "^5.6.0", "rollup-plugin-visualizer": "^5.6.0",
"sass": "~1.42.1", "sass": "~1.42.1",
"stylelint": "^15.10.1", "stylelint": "^15.10.1",
"stylelint-config-prettier": "^9.0.5", "stylelint-config-prettier": "^9.0.5",
"stylelint-config-standard": "~25.0.0", "stylelint-config-standard": "~25.0.0",
"terser": "~5.15.0", "terser": "~5.15.0",
"test": "~3.3.0", "test": "~3.3.0",
"testcontainers": "^10.13.1", "testcontainers": "^10.13.1",
"typescript": "~4.4.4", "typescript": "~4.4.4",
"v-pagination-3": "~0.1.7", "v-pagination-3": "~0.1.7",
"vite": "~5.4.15", "vite": "~5.4.15",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^1.1.0", "vite-plugin-pwa": "^1.1.0",
"vue": "~3.4.2", "vue": "~3.4.2",
"vue-chartjs": "~5.2.0", "vue-chartjs": "~5.2.0",
"vue-confirm-dialog": "~1.0.2", "vue-confirm-dialog": "~1.0.2",
"vue-contenteditable": "~3.0.4", "vue-contenteditable": "~3.0.4",
"vue-i18n": "~9.14.3", "vue-i18n": "~9.14.3",
"vue-image-crop-upload": "~3.0.3", "vue-image-crop-upload": "~3.0.3",
"vue-multiselect": "~3.0.0-alpha.2", "vue-multiselect": "~3.0.0-alpha.2",
"vue-prism-editor": "~2.0.0-alpha.2", "vue-prism-editor": "~2.0.0-alpha.2",
"vue-qrcode": "~1.0.0", "vue-qrcode": "~1.0.0",
"vue-router": "~4.2.5", "vue-router": "~4.2.5",
"vue-toastification": "~2.0.0-rc.5", "vue-toastification": "~2.0.0-rc.5",
"vuedraggable": "~4.1.0", "vuedraggable": "~4.1.0",
"wait-on": "^7.2.0", "wait-on": "^7.2.0",
"whatwg-url": "~12.0.1" "whatwg-url": "~12.0.1"
} }
} }

View File

@ -1,88 +1,88 @@
{ {
"name": "Uptime Kuma", "name": "Uptime Kuma",
"short_name": "Uptime Kuma", "short_name": "Uptime Kuma",
"description": "An easy-to-use self-hosted monitoring tool.", "description": "An easy-to-use self-hosted monitoring tool.",
"theme_color": "#5cdd8b", "theme_color": "#5cdd8b",
"start_url": "/", "start_url": "/",
"background_color": "#fff", "background_color": "#fff",
"display": "standalone", "display": "standalone",
"icons": [ "icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"shortcuts": [
{
"name": "Dashboard",
"short_name": "Dashboard",
"description": "View monitoring dashboard",
"url": "/dashboard",
"icons": [
{ {
"src": "icon-192x192.png", "src": "icon-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
} }
], ]
"shortcuts": [ },
{
"name": "Add Monitor",
"short_name": "Add Monitor",
"description": "Add a new monitor",
"url": "/add",
"icons": [
{ {
"name": "Dashboard", "src": "icon-192x192.png",
"short_name": "Dashboard", "sizes": "192x192",
"description": "View monitoring dashboard", "type": "image/png"
"url": "/dashboard",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Add Monitor",
"short_name": "Add Monitor",
"description": "Add a new monitor",
"url": "/add",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Monitor List",
"short_name": "List",
"description": "View all monitors",
"url": "/list",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Settings",
"short_name": "Settings",
"description": "Open settings",
"url": "/settings",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Maintenance",
"short_name": "Maintenance",
"description": "Manage maintenance windows",
"url": "/maintenance",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
} }
] ]
},
{
"name": "Monitor List",
"short_name": "List",
"description": "View all monitors",
"url": "/list",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Settings",
"short_name": "Settings",
"description": "Open settings",
"url": "/settings",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "Maintenance",
"short_name": "Maintenance",
"description": "Manage maintenance windows",
"url": "/maintenance",
"icons": [
{
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
]
} }

View File

@ -1,18 +1,14 @@
const { R } = require("redbean-node"); const { R } = require("redbean-node");
class TwoFA { class TwoFA {
/** /**
* Disable 2FA for specified user * Disable 2FA for specified user
* @param {number} userID ID of user to disable * @param {number} userID ID of user to disable
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async disable2FA(userID) { static async disable2FA(userID) {
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [ return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [userID]);
userID,
]);
} }
} }
module.exports = TwoFA; module.exports = TwoFA;

View File

@ -16,7 +16,10 @@ function getAnalyticsScript(statusPage) {
case "umami": case "umami":
return umamiAnalytics.getUmamiAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId); return umamiAnalytics.getUmamiAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId);
case "plausible": case "plausible":
return plausibleAnalytics.getPlausibleAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId); return plausibleAnalytics.getPlausibleAnalyticsScript(
statusPage.analyticsScriptUrl,
statusPage.analyticsId
);
case "matomo": case "matomo":
return matomoAnalytics.getMatomoAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId); return matomoAnalytics.getMatomoAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId);
default: default:
@ -44,5 +47,5 @@ function isValidAnalyticsConfig(statusPage) {
module.exports = { module.exports = {
getAnalyticsScript, getAnalyticsScript,
isValidAnalyticsConfig isValidAnalyticsConfig,
}; };

View File

@ -32,5 +32,5 @@ function getPlausibleAnalyticsScript(scriptUrl, domainsToMonitor) {
} }
module.exports = { module.exports = {
getPlausibleAnalyticsScript getPlausibleAnalyticsScript,
}; };

View File

@ -18,9 +18,7 @@ exports.login = async function (username, password) {
return null; return null;
} }
let user = await R.findOne("user", "TRIM(username) = ? AND active = 1 ", [ let user = await R.findOne("user", "TRIM(username) = ? AND active = 1 ", [username.trim()]);
username.trim(),
]);
if (user && passwordHash.verify(password, user.password)) { if (user && passwordHash.verify(password, user.password)) {
// Upgrade the hash to bcrypt // Upgrade the hash to bcrypt
@ -50,7 +48,7 @@ async function verifyAPIKey(key) {
let index = key.substring(2, key.indexOf("_")); let index = key.substring(2, key.indexOf("_"));
let clear = key.substring(key.indexOf("_") + 1, key.length); let clear = key.substring(key.indexOf("_") + 1, key.length);
let hash = await R.findOne("api_key", " id=? ", [ index ]); let hash = await R.findOne("api_key", " id=? ", [index]);
if (hash === null) { if (hash === null) {
return false; return false;
@ -156,7 +154,7 @@ exports.basicAuth = async function (req, res, next) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
exports.apiAuth = async function (req, res, next) { exports.apiAuth = async function (req, res, next) {
if (!await Settings.get("disableAuth")) { if (!(await Settings.get("disableAuth"))) {
let usingAPIKeys = await Settings.get("apiKeysEnabled"); let usingAPIKeys = await Settings.get("apiKeysEnabled");
let middleware; let middleware;
if (usingAPIKeys) { if (usingAPIKeys) {

View File

@ -14,7 +14,7 @@ let interval;
exports.startInterval = () => { exports.startInterval = () => {
let check = async () => { let check = async () => {
if (await setting("checkUpdate") === false) { if ((await setting("checkUpdate")) === false) {
return; return;
} }
@ -40,11 +40,9 @@ exports.startInterval = () => {
if (res.data.slow) { if (res.data.slow) {
exports.latestVersion = res.data.slow; exports.latestVersion = res.data.slow;
} }
} catch (_) { } catch (_) {
log.info("update-checker", "Failed to check for new versions"); log.info("update-checker", "Failed to check for new versions");
} }
}; };
check(); check();

View File

@ -19,14 +19,12 @@ async function sendNotificationList(socket) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
let result = []; let result = [];
let list = await R.find("notification", " user_id = ? ", [ let list = await R.find("notification", " user_id = ? ", [socket.userID]);
socket.userID,
]);
for (let bean of list) { for (let bean of list) {
let notificationObject = bean.export(); let notificationObject = bean.export();
notificationObject.isDefault = (notificationObject.isDefault === 1); notificationObject.isDefault = notificationObject.isDefault === 1;
notificationObject.active = (notificationObject.active === 1); notificationObject.active = notificationObject.active === 1;
result.push(notificationObject); result.push(notificationObject);
} }
@ -46,14 +44,15 @@ async function sendNotificationList(socket) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
let list = await R.getAll(` let list = await R.getAll(
`
SELECT * FROM heartbeat SELECT * FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
ORDER BY time DESC ORDER BY time DESC
LIMIT 100 LIMIT 100
`, [ `,
monitorID, [monitorID]
]); );
let result = list.reverse(); let result = list.reverse();
@ -75,14 +74,16 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite =
async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
let list = await R.find("heartbeat", ` let list = await R.find(
"heartbeat",
`
monitor_id = ? monitor_id = ?
AND important = 1 AND important = 1
ORDER BY time DESC ORDER BY time DESC
LIMIT 500 LIMIT 500
`, [ `,
monitorID, [monitorID]
]); );
timeLogger.print(`[Monitor: ${monitorID}] sendImportantHeartbeatList`); timeLogger.print(`[Monitor: ${monitorID}] sendImportantHeartbeatList`);
@ -91,7 +92,6 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove
} else { } else {
socket.emit("importantHeartbeatList", monitorID, list, overwrite); socket.emit("importantHeartbeatList", monitorID, list, overwrite);
} }
} }
/** /**
@ -102,8 +102,11 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove
async function sendProxyList(socket) { async function sendProxyList(socket) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
const list = await R.find("proxy", " user_id = ? ", [ socket.userID ]); const list = await R.find("proxy", " user_id = ? ", [socket.userID]);
io.to(socket.userID).emit("proxyList", list.map(bean => bean.export())); io.to(socket.userID).emit(
"proxyList",
list.map((bean) => bean.export())
);
timeLogger.print("Send Proxy List"); timeLogger.print("Send Proxy List");
@ -119,11 +122,7 @@ async function sendAPIKeyList(socket) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
let result = []; let result = [];
const list = await R.find( const list = await R.find("api_key", "user_id=?", [socket.userID]);
"api_key",
"user_id=?",
[ socket.userID ],
);
for (let bean of list) { for (let bean of list) {
result.push(bean.toPublicJSON()); result.push(bean.toPublicJSON());
@ -150,7 +149,7 @@ async function sendInfo(socket, hideVersion = false) {
if (!hideVersion) { if (!hideVersion) {
info.version = checkVersion.version; info.version = checkVersion.version;
info.latestVersion = checkVersion.latestVersion; info.latestVersion = checkVersion.latestVersion;
info.isContainer = (process.env.UPTIME_KUMA_IS_CONTAINER === "1"); info.isContainer = process.env.UPTIME_KUMA_IS_CONTAINER === "1";
info.dbType = Database.dbConfig.type; info.dbType = Database.dbConfig.type;
info.runtime = { info.runtime = {
platform: process.platform, // linux or win32 platform: process.platform, // linux or win32
@ -170,9 +169,7 @@ async function sendDockerHostList(socket) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
let result = []; let result = [];
let list = await R.find("docker_host", " user_id = ? ", [ let list = await R.find("docker_host", " user_id = ? ", [socket.userID]);
socket.userID,
]);
for (let bean of list) { for (let bean of list) {
result.push(bean.toJSON()); result.push(bean.toJSON());
@ -194,9 +191,7 @@ async function sendRemoteBrowserList(socket) {
const timeLogger = new TimeLogger(); const timeLogger = new TimeLogger();
let result = []; let result = [];
let list = await R.find("remote_browser", " user_id = ? ", [ let list = await R.find("remote_browser", " user_id = ? ", [socket.userID]);
socket.userID,
]);
for (let bean of list) { for (let bean of list) {
result.push(bean.toJSON()); result.push(bean.toJSON());
@ -215,21 +210,24 @@ async function sendRemoteBrowserList(socket) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function sendMonitorTypeList(socket) { async function sendMonitorTypeList(socket) {
const result = Object.entries(UptimeKumaServer.monitorTypeList).map(([ key, type ]) => { const result = Object.entries(UptimeKumaServer.monitorTypeList).map(([key, type]) => {
return [ key, { return [
supportsConditions: type.supportsConditions, key,
conditionVariables: type.conditionVariables.map(v => { {
return { supportsConditions: type.supportsConditions,
id: v.id, conditionVariables: type.conditionVariables.map((v) => {
operators: v.operators.map(o => { return {
return { id: v.id,
id: o.id, operators: v.operators.map((o) => {
caption: o.caption, return {
}; id: o.id,
}), caption: o.caption,
}; };
}), }),
}]; };
}),
},
];
}); });
io.to(socket.userID).emit("monitorTypeList", Object.fromEntries(result)); io.to(socket.userID).emit("monitorTypeList", Object.fromEntries(result));

View File

@ -1,7 +1,7 @@
const isFreeBSD = /^freebsd/.test(process.platform); const isFreeBSD = /^freebsd/.test(process.platform);
// Interop with browser // Interop with browser
const args = (typeof process !== "undefined") ? require("args-parser")(process.argv) : {}; const args = typeof process !== "undefined" ? require("args-parser")(process.argv) : {};
// If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise. // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise.
// Dual-stack support for (::) // Dual-stack support for (::)
@ -9,13 +9,17 @@ const args = (typeof process !== "undefined") ? require("args-parser")(process.a
let hostEnv = isFreeBSD ? null : process.env.HOST; let hostEnv = isFreeBSD ? null : process.env.HOST;
const hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv; const hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv;
const port = [ args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001 ] const port = [args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001]
.map(portValue => parseInt(portValue)) .map((portValue) => parseInt(portValue))
.find(portValue => !isNaN(portValue)); .find((portValue) => !isNaN(portValue));
const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined; const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined;
const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined; const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined;
const sslKeyPassphrase = args["ssl-key-passphrase"] || process.env.UPTIME_KUMA_SSL_KEY_PASSPHRASE || process.env.SSL_KEY_PASSPHRASE || undefined; const sslKeyPassphrase =
args["ssl-key-passphrase"] ||
process.env.UPTIME_KUMA_SSL_KEY_PASSPHRASE ||
process.env.SSL_KEY_PASSPHRASE ||
undefined;
const isSSL = sslKey && sslCert; const isSSL = sslKey && sslCert;

View File

@ -18,7 +18,6 @@ const SqlString = require("sqlstring");
* Database & App Data Folder * Database & App Data Folder
*/ */
class Database { class Database {
/** /**
* Bootstrap database for SQLite * Bootstrap database for SQLite
* @type {string} * @type {string}
@ -89,7 +88,7 @@ class Database {
"patch-added-mqtt-monitor.sql": true, "patch-added-mqtt-monitor.sql": true,
"patch-add-clickable-status-page-link.sql": true, "patch-add-clickable-status-page-link.sql": true,
"patch-add-sqlserver-monitor.sql": true, "patch-add-sqlserver-monitor.sql": true,
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] }, "patch-add-other-auth.sql": { parents: ["patch-monitor-basic-auth.sql"] },
"patch-grpc-monitor.sql": true, "patch-grpc-monitor.sql": true,
"patch-add-radius-monitor.sql": true, "patch-add-radius-monitor.sql": true,
"patch-monitor-add-resend-interval.sql": true, "patch-monitor-add-resend-interval.sql": true,
@ -138,24 +137,24 @@ class Database {
Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/"; Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/";
Database.sqlitePath = path.join(Database.dataDir, "kuma.db"); Database.sqlitePath = path.join(Database.dataDir, "kuma.db");
if (! fs.existsSync(Database.dataDir)) { if (!fs.existsSync(Database.dataDir)) {
fs.mkdirSync(Database.dataDir, { recursive: true }); fs.mkdirSync(Database.dataDir, { recursive: true });
} }
Database.uploadDir = path.join(Database.dataDir, "upload/"); Database.uploadDir = path.join(Database.dataDir, "upload/");
if (! fs.existsSync(Database.uploadDir)) { if (!fs.existsSync(Database.uploadDir)) {
fs.mkdirSync(Database.uploadDir, { recursive: true }); fs.mkdirSync(Database.uploadDir, { recursive: true });
} }
// Create screenshot dir // Create screenshot dir
Database.screenshotDir = path.join(Database.dataDir, "screenshots/"); Database.screenshotDir = path.join(Database.dataDir, "screenshots/");
if (! fs.existsSync(Database.screenshotDir)) { if (!fs.existsSync(Database.screenshotDir)) {
fs.mkdirSync(Database.screenshotDir, { recursive: true }); fs.mkdirSync(Database.screenshotDir, { recursive: true });
} }
Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/"); Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/");
if (! fs.existsSync(Database.dockerTLSDir)) { if (!fs.existsSync(Database.dockerTLSDir)) {
fs.mkdirSync(Database.dockerTLSDir, { recursive: true }); fs.mkdirSync(Database.dockerTLSDir, { recursive: true });
} }
@ -228,13 +227,22 @@ class Database {
if (!process.env.UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS) { if (!process.env.UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS) {
parsedMaxPoolConnections = 10; parsedMaxPoolConnections = 10;
} else if (Number.isNaN(parsedMaxPoolConnections)) { } else if (Number.isNaN(parsedMaxPoolConnections)) {
log.warn("db", "Max database connections defaulted to 10 because UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS was invalid."); log.warn(
"db",
"Max database connections defaulted to 10 because UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS was invalid."
);
parsedMaxPoolConnections = 10; parsedMaxPoolConnections = 10;
} else if (parsedMaxPoolConnections < 1) { } else if (parsedMaxPoolConnections < 1) {
log.warn("db", "Max database connections defaulted to 10 because UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS was less than 1."); log.warn(
"db",
"Max database connections defaulted to 10 because UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS was less than 1."
);
parsedMaxPoolConnections = 10; parsedMaxPoolConnections = 10;
} else if (parsedMaxPoolConnections > 100) { } else if (parsedMaxPoolConnections > 100) {
log.warn("db", "Max database connections capped to 100 because Mysql/Mariadb connections are heavy. consider using a proxy like ProxySQL or MaxScale."); log.warn(
"db",
"Max database connections capped to 100 because Mysql/Mariadb connections are heavy. consider using a proxy like ProxySQL or MaxScale."
);
parsedMaxPoolConnections = 100; parsedMaxPoolConnections = 100;
} }
@ -247,8 +255,7 @@ class Database {
log.info("db", `Database Type: ${dbConfig.type}`); log.info("db", `Database Type: ${dbConfig.type}`);
if (dbConfig.type === "sqlite") { if (dbConfig.type === "sqlite") {
if (!fs.existsSync(Database.sqlitePath)) {
if (! fs.existsSync(Database.sqlitePath)) {
log.info("server", "Copying Database"); log.info("server", "Copying Database");
fs.copyFileSync(Database.templatePath, Database.sqlitePath); fs.copyFileSync(Database.templatePath, Database.sqlitePath);
} }
@ -269,7 +276,7 @@ class Database {
idleTimeoutMillis: 120 * 1000, idleTimeoutMillis: 120 * 1000,
propagateCreateError: false, propagateCreateError: false,
acquireTimeoutMillis: acquireConnectionTimeout, acquireTimeoutMillis: acquireConnectionTimeout,
} },
}; };
} else if (dbConfig.type === "mariadb") { } else if (dbConfig.type === "mariadb") {
const connection = await mysql.createConnection({ const connection = await mysql.createConnection({
@ -387,7 +394,7 @@ class Database {
log.debug("db", "SQLite config:"); log.debug("db", "SQLite config:");
log.debug("db", await R.getAll("PRAGMA journal_mode")); log.debug("db", await R.getAll("PRAGMA journal_mode"));
log.debug("db", await R.getAll("PRAGMA cache_size")); log.debug("db", await R.getAll("PRAGMA cache_size"));
log.debug("db", "SQLite Version: " + await R.getCell("SELECT sqlite_version()")); log.debug("db", "SQLite Version: " + (await R.getCell("SELECT sqlite_version()")));
} }
} }
@ -439,7 +446,6 @@ class Database {
} }
await this.migrateAggregateTable(port, hostname); await this.migrateAggregateTable(port, hostname);
} catch (e) { } catch (e) {
// Allow missing patch files for downgrade or testing pr. // Allow missing patch files for downgrade or testing pr.
if (e.message.includes("the following files are missing:")) { if (e.message.includes("the following files are missing:")) {
@ -456,9 +462,7 @@ class Database {
* TODO * TODO
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async rollbackLatestPatch() { static async rollbackLatestPatch() {}
}
/** /**
* Patch the database for SQLite * Patch the database for SQLite
@ -468,7 +472,7 @@ class Database {
static async patchSqlite() { static async patchSqlite() {
let version = parseInt(await setting("database_version")); let version = parseInt(await setting("database_version"));
if (! version) { if (!version) {
version = 0; version = 0;
} }
@ -498,7 +502,10 @@ class Database {
log.error("db", ex); log.error("db", ex);
log.error("db", "Start Uptime-Kuma failed due to issue patching the database"); log.error("db", "Start Uptime-Kuma failed due to issue patching the database");
log.error("db", "Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); log.error(
"db",
"Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"
);
process.exit(1); process.exit(1);
} }
@ -519,7 +526,7 @@ class Database {
log.debug("db", "Database Patch 2.0 Process"); log.debug("db", "Database Patch 2.0 Process");
let databasePatchedFiles = await setting("databasePatchedFiles"); let databasePatchedFiles = await setting("databasePatchedFiles");
if (! databasePatchedFiles) { if (!databasePatchedFiles) {
databasePatchedFiles = {}; databasePatchedFiles = {};
} }
@ -534,13 +541,15 @@ class Database {
if (this.patched) { if (this.patched) {
log.info("db", "Database Patched Successfully"); log.info("db", "Database Patched Successfully");
} }
} catch (ex) { } catch (ex) {
await Database.close(); await Database.close();
log.error("db", ex); log.error("db", ex);
log.error("db", "Start Uptime-Kuma failed due to issue patching the database"); log.error("db", "Start Uptime-Kuma failed due to issue patching the database");
log.error("db", "Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); log.error(
"db",
"Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"
);
process.exit(1); process.exit(1);
} }
@ -554,7 +563,6 @@ class Database {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async migrateNewStatusPage() { static async migrateNewStatusPage() {
// Fix 1.13.0 empty slug bug // Fix 1.13.0 empty slug bug
await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''"); await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''");
@ -576,9 +584,9 @@ class Database {
statusPage.description = await setting("description"); statusPage.description = await setting("description");
statusPage.icon = await setting("icon"); statusPage.icon = await setting("icon");
statusPage.theme = await setting("statusPageTheme"); statusPage.theme = await setting("statusPageTheme");
statusPage.published = !!await setting("statusPagePublished"); statusPage.published = !!(await setting("statusPagePublished"));
statusPage.search_engine_index = !!await setting("searchEngineIndex"); statusPage.search_engine_index = !!(await setting("searchEngineIndex"));
statusPage.show_tags = !!await setting("statusPageTags"); statusPage.show_tags = !!(await setting("statusPageTags"));
statusPage.password = null; statusPage.password = null;
if (!statusPage.title) { if (!statusPage.title) {
@ -595,13 +603,9 @@ class Database {
let id = await R.store(statusPage); let id = await R.store(statusPage);
await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [ await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [id]);
id
]);
await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [ await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [id]);
id
]);
await R.exec("DELETE FROM setting WHERE type = 'statusPage'"); await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
@ -614,7 +618,6 @@ class Database {
console.log("Migrating Status Page - Done"); console.log("Migrating Status Page - Done");
} }
} }
/** /**
@ -628,13 +631,13 @@ class Database {
static async patch2Recursion(sqlFilename, databasePatchedFiles) { static async patch2Recursion(sqlFilename, databasePatchedFiles) {
let value = this.patchList[sqlFilename]; let value = this.patchList[sqlFilename];
if (! value) { if (!value) {
log.info("db", sqlFilename + " skip"); log.info("db", sqlFilename + " skip");
return; return;
} }
// Check if patched // Check if patched
if (! databasePatchedFiles[sqlFilename]) { if (!databasePatchedFiles[sqlFilename]) {
log.info("db", sqlFilename + " is not patched"); log.info("db", sqlFilename + " is not patched");
if (value.parents) { if (value.parents) {
@ -649,7 +652,6 @@ class Database {
await this.importSQLFile("./db/old_migrations/" + sqlFilename); await this.importSQLFile("./db/old_migrations/" + sqlFilename);
databasePatchedFiles[sqlFilename] = true; databasePatchedFiles[sqlFilename] = true;
log.info("db", sqlFilename + " was patched successfully"); log.info("db", sqlFilename + " was patched successfully");
} else { } else {
log.debug("db", sqlFilename + " is already patched, skip"); log.debug("db", sqlFilename + " is already patched, skip");
} }
@ -669,14 +671,15 @@ class Database {
// Remove all comments (--) // Remove all comments (--)
let lines = text.split("\n"); let lines = text.split("\n");
lines = lines.filter((line) => { lines = lines.filter((line) => {
return ! line.startsWith("--"); return !line.startsWith("--");
}); });
// Split statements by semicolon // Split statements by semicolon
// Filter out empty line // Filter out empty line
text = lines.join("\n"); text = lines.join("\n");
let statements = text.split(";") let statements = text
.split(";")
.map((statement) => { .map((statement) => {
return statement.trim(); return statement.trim();
}) })
@ -773,7 +776,10 @@ class Database {
// Add a setting for 2.0.0-dev users to skip this migration // Add a setting for 2.0.0-dev users to skip this migration
if (process.env.SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE === "1") { if (process.env.SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE === "1") {
log.warn("db", "SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE is set to 1, skipping aggregate table migration forever (for 2.0.0-dev users)"); log.warn(
"db",
"SET_MIGRATE_AGGREGATE_TABLE_TO_TRUE is set to 1, skipping aggregate table migration forever (for 2.0.0-dev users)"
);
await Settings.set("migrateAggregateTableState", "migrated"); await Settings.set("migrateAggregateTableState", "migrated");
} }
@ -813,11 +819,14 @@ class Database {
`); `);
// Stop if stat_* tables are not empty // Stop if stat_* tables are not empty
for (let table of [ "stat_minutely", "stat_hourly", "stat_daily" ]) { for (let table of ["stat_minutely", "stat_hourly", "stat_daily"]) {
let countResult = await R.getRow(`SELECT COUNT(*) AS count FROM ${table}`); let countResult = await R.getRow(`SELECT COUNT(*) AS count FROM ${table}`);
let count = countResult.count; let count = countResult.count;
if (count > 0) { if (count > 0) {
log.warn("db", `Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`); log.warn(
"db",
`Aggregate table ${table} is not empty, migration will not be started (Maybe you were using 2.0.0-dev?)`
);
await migrationServer?.stop(); await migrationServer?.stop();
return; return;
} }
@ -826,31 +835,35 @@ class Database {
await Settings.set("migrateAggregateTableState", "migrating"); await Settings.set("migrateAggregateTableState", "migrating");
let progressPercent = 0; let progressPercent = 0;
for (const [ i, monitor ] of monitors.entries()) { for (const [i, monitor] of monitors.entries()) {
// Get a list of unique dates from the heartbeat table, using raw sql // Get a list of unique dates from the heartbeat table, using raw sql
let dates = await R.getAll(` let dates = await R.getAll(
`
SELECT DISTINCT DATE(time) AS date SELECT DISTINCT DATE(time) AS date
FROM heartbeat FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
ORDER BY date ASC ORDER BY date ASC
`, [ `,
monitor.monitor_id [monitor.monitor_id]
]); );
for (const [ dateIndex, date ] of dates.entries()) { for (const [dateIndex, date] of dates.entries()) {
// New Uptime Calculator // New Uptime Calculator
let calculator = new UptimeCalculator(); let calculator = new UptimeCalculator();
calculator.monitorID = monitor.monitor_id; calculator.monitorID = monitor.monitor_id;
calculator.setMigrationMode(true); calculator.setMigrationMode(true);
// Get all the heartbeats for this monitor and date // Get all the heartbeats for this monitor and date
let heartbeats = await R.getAll(` let heartbeats = await R.getAll(
`
SELECT status, ping, time SELECT status, ping, time
FROM heartbeat FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
AND DATE(time) = ? AND DATE(time) = ?
ORDER BY time ASC ORDER BY time ASC
`, [ monitor.monitor_id, date.date ]); `,
[monitor.monitor_id, date.date]
);
if (heartbeats.length > 0) { if (heartbeats.length > 0) {
msg = `[DON'T STOP] Migrating monitor ${monitor.monitor_id}s' (${i + 1} of ${monitors.length} total) data - ${date.date} - total migration progress ${progressPercent.toFixed(2)}%`; msg = `[DON'T STOP] Migrating monitor ${monitor.monitor_id}s' (${i + 1} of ${monitors.length} total) data - ${date.date} - total migration progress ${progressPercent.toFixed(2)}%`;
@ -863,7 +876,7 @@ class Database {
} }
// Calculate progress: (current_monitor_index + relative_date_progress) / total_monitors // Calculate progress: (current_monitor_index + relative_date_progress) / total_monitors
progressPercent = (i + (dateIndex + 1) / dates.length) / monitors.length * 100; progressPercent = ((i + (dateIndex + 1) / dates.length) / monitors.length) * 100;
// Lazy to fix the floating point issue, it is acceptable since it is just a progress bar // Lazy to fix the floating point issue, it is acceptable since it is just a progress bar
if (progressPercent > 100) { if (progressPercent > 100) {
@ -900,7 +913,8 @@ class Database {
if (detailedLog) { if (detailedLog) {
log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id); log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id);
} }
await R.exec(` await R.exec(
`
DELETE FROM heartbeat DELETE FROM heartbeat
WHERE monitor_id = ? WHERE monitor_id = ?
AND important = 0 AND important = 0
@ -914,15 +928,11 @@ class Database {
LIMIT ? LIMIT ?
) AS limited_ids ) AS limited_ids
) )
`, [ `,
monitor.id, [monitor.id, -24, monitor.id, 100]
-24, );
monitor.id,
100,
]);
} }
} }
} }
module.exports = Database; module.exports = Database;

View File

@ -7,7 +7,6 @@ const Database = require("./database");
const { axiosAbortSignal, fsExists } = require("./util-server"); const { axiosAbortSignal, fsExists } = require("./util-server");
class DockerHost { class DockerHost {
static CertificateFileNameCA = "ca.pem"; static CertificateFileNameCA = "ca.pem";
static CertificateFileNameCert = "cert.pem"; static CertificateFileNameCert = "cert.pem";
static CertificateFileNameKey = "key.pem"; static CertificateFileNameKey = "key.pem";
@ -23,12 +22,11 @@ class DockerHost {
let bean; let bean;
if (dockerHostID) { if (dockerHostID) {
bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]); bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [dockerHostID, userID]);
if (!bean) { if (!bean) {
throw new Error("docker host not found"); throw new Error("docker host not found");
} }
} else { } else {
bean = R.dispense("docker_host"); bean = R.dispense("docker_host");
} }
@ -50,14 +48,14 @@ class DockerHost {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async delete(dockerHostID, userID) { static async delete(dockerHostID, userID) {
let bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [ dockerHostID, userID ]); let bean = await R.findOne("docker_host", " id = ? AND user_id = ? ", [dockerHostID, userID]);
if (!bean) { if (!bean) {
throw new Error("docker host not found"); throw new Error("docker host not found");
} }
// Delete removed proxy from monitors if exists // Delete removed proxy from monitors if exists
await R.exec("UPDATE monitor SET docker_host = null WHERE docker_host = ?", [ dockerHostID ]); await R.exec("UPDATE monitor SET docker_host = null WHERE docker_host = ?", [dockerHostID]);
await R.trash(bean); await R.trash(bean);
} }
@ -72,7 +70,7 @@ class DockerHost {
url: "/containers/json?all=true", url: "/containers/json?all=true",
timeout: 5000, timeout: 5000,
headers: { headers: {
"Accept": "*/*", Accept: "*/*",
}, },
signal: axiosAbortSignal(6000), signal: axiosAbortSignal(6000),
}; };
@ -81,26 +79,24 @@ class DockerHost {
options.socketPath = dockerHost.dockerDaemon; options.socketPath = dockerHost.dockerDaemon;
} else if (dockerHost.dockerType === "tcp") { } else if (dockerHost.dockerType === "tcp") {
options.baseURL = DockerHost.patchDockerURL(dockerHost.dockerDaemon); options.baseURL = DockerHost.patchDockerURL(dockerHost.dockerDaemon);
options.httpsAgent = new https.Agent(await DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL)); options.httpsAgent = new https.Agent(
await DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL)
);
} }
try { try {
let res = await axios.request(options); let res = await axios.request(options);
if (Array.isArray(res.data)) { if (Array.isArray(res.data)) {
if (res.data.length > 1) { if (res.data.length > 1) {
if ("ImageID" in res.data[0]) { if ("ImageID" in res.data[0]) {
return res.data.length; return res.data.length;
} else { } else {
throw new Error("Invalid Docker response, is it Docker really a daemon?"); throw new Error("Invalid Docker response, is it Docker really a daemon?");
} }
} else { } else {
return res.data.length; return res.data.length;
} }
} else { } else {
throw new Error("Invalid Docker response, is it Docker really a daemon?"); throw new Error("Invalid Docker response, is it Docker really a daemon?");
} }
@ -146,30 +142,35 @@ class DockerHost {
static async getHttpsAgentOptions(dockerType, url) { static async getHttpsAgentOptions(dockerType, url) {
let baseOptions = { let baseOptions = {
maxCachedSessions: 0, maxCachedSessions: 0,
rejectUnauthorized: true rejectUnauthorized: true,
}; };
let certOptions = {}; let certOptions = {};
let dirName = (new URL(url)).hostname; let dirName = new URL(url).hostname;
let caPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameCA); let caPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameCA);
let certPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameCert); let certPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameCert);
let keyPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameKey); let keyPath = path.join(Database.dockerTLSDir, dirName, DockerHost.CertificateFileNameKey);
if (dockerType === "tcp" && await fsExists(caPath) && await fsExists(certPath) && await fsExists(keyPath)) { if (
dockerType === "tcp" &&
(await fsExists(caPath)) &&
(await fsExists(certPath)) &&
(await fsExists(keyPath))
) {
let ca = await fsAsync.readFile(caPath); let ca = await fsAsync.readFile(caPath);
let key = await fsAsync.readFile(keyPath); let key = await fsAsync.readFile(keyPath);
let cert = await fsAsync.readFile(certPath); let cert = await fsAsync.readFile(certPath);
certOptions = { certOptions = {
ca, ca,
key, key,
cert cert,
}; };
} }
return { return {
...baseOptions, ...baseOptions,
...certOptions ...certOptions,
}; };
} }
} }

View File

@ -7,7 +7,6 @@ const mysql = require("mysql2");
* It is only used inside the docker container * It is only used inside the docker container
*/ */
class EmbeddedMariaDB { class EmbeddedMariaDB {
static instance = null; static instance = null;
exec = "mariadbd"; exec = "mariadbd";
@ -59,7 +58,9 @@ class EmbeddedMariaDB {
// Check if the current user is "node" or "root" // Check if the current user is "node" or "root"
this.username = require("os").userInfo().username; this.username = require("os").userInfo().username;
if (this.username !== "node" && this.username !== "root") { if (this.username !== "node" && this.username !== "root") {
throw new Error("Embedded Mariadb supports only 'node' or 'root' user, but the current user is: " + this.username); throw new Error(
"Embedded Mariadb supports only 'node' or 'root' user, but the current user is: " + this.username
);
} }
this.initDB(); this.initDB();
@ -211,7 +212,6 @@ class EmbeddedMariaDB {
log.info("mariadb", "Embedded MariaDB is ready for connections"); log.info("mariadb", "Embedded MariaDB is ready for connections");
this.started = true; this.started = true;
} }
} }
module.exports = { module.exports = {

View File

@ -6,7 +6,6 @@ let fs = require("fs");
const { log } = require("../src/util"); const { log } = require("../src/util");
let ImageDataURI = (() => { let ImageDataURI = (() => {
/** /**
* Decode the data:image/ URI * Decode the data:image/ URI
* @param {string} dataURI data:image/ URI to decode * @param {string} dataURI data:image/ URI to decode
@ -17,7 +16,7 @@ let ImageDataURI = (() => {
*/ */
function decode(dataURI) { function decode(dataURI) {
if (!/data:image\//.test(dataURI)) { if (!/data:image\//.test(dataURI)) {
log.error("image-data-uri", "It seems that it is not an Image Data URI. Couldn't match \"data:image/\""); log.error("image-data-uri", 'It seems that it is not an Image Data URI. Couldn\'t match "data:image/"');
return null; return null;
} }
@ -25,7 +24,7 @@ let ImageDataURI = (() => {
return { return {
imageType: regExMatches[1], imageType: regExMatches[1],
dataBase64: regExMatches[2], dataBase64: regExMatches[2],
dataBuffer: new Buffer(regExMatches[2], "base64") dataBuffer: new Buffer(regExMatches[2], "base64"),
}; };
} }
@ -42,8 +41,8 @@ let ImageDataURI = (() => {
return null; return null;
} }
mediaType = (/\//.test(mediaType)) ? mediaType : "image/" + mediaType; mediaType = /\//.test(mediaType) ? mediaType : "image/" + mediaType;
let dataBase64 = (Buffer.isBuffer(data)) ? data.toString("base64") : new Buffer(data).toString("base64"); let dataBase64 = Buffer.isBuffer(data) ? data.toString("base64") : new Buffer(data).toString("base64");
let dataImgBase64 = "data:" + mediaType + ";base64," + dataBase64; let dataImgBase64 = "data:" + mediaType + ";base64," + dataBase64;
return dataImgBase64; return dataImgBase64;
@ -60,7 +59,7 @@ let ImageDataURI = (() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let imageDecoded = decode(dataURI); let imageDecoded = decode(dataURI);
fs.writeFile(filePath, imageDecoded.dataBuffer, err => { fs.writeFile(filePath, imageDecoded.dataBuffer, (err) => {
if (err) { if (err) {
return reject("ImageDataURI :: Error :: " + JSON.stringify(err, null, 4)); return reject("ImageDataURI :: Error :: " + JSON.stringify(err, null, 4));
} }

View File

@ -15,7 +15,7 @@ const jobs = [
interval: "*/5 * * * *", interval: "*/5 * * * *",
jobFunc: incrementalVacuum, jobFunc: incrementalVacuum,
croner: null, croner: null,
} },
]; ];
/** /**
@ -32,11 +32,10 @@ const initBackgroundJobs = async function () {
name: job.name, name: job.name,
timezone, timezone,
}, },
job.jobFunc, job.jobFunc
); );
job.croner = cornerJob; job.croner = cornerJob;
} }
}; };
/** /**
@ -54,5 +53,5 @@ const stopBackgroundJobs = function () {
module.exports = { module.exports = {
initBackgroundJobs, initBackgroundJobs,
stopBackgroundJobs stopBackgroundJobs,
}; };

View File

@ -31,23 +31,22 @@ const clearOldData = async () => {
} }
if (parsedPeriod < 1) { if (parsedPeriod < 1) {
log.info("clearOldData", `Data deletion has been disabled as period is less than 1. Period is ${parsedPeriod} days.`); log.info(
"clearOldData",
`Data deletion has been disabled as period is less than 1. Period is ${parsedPeriod} days.`
);
} else { } else {
log.debug("clearOldData", `Clearing Data older than ${parsedPeriod} days...`); log.debug("clearOldData", `Clearing Data older than ${parsedPeriod} days...`);
const sqlHourOffset = Database.sqlHourOffset(); const sqlHourOffset = Database.sqlHourOffset();
try { try {
// Heartbeat // Heartbeat
await R.exec("DELETE FROM heartbeat WHERE time < " + sqlHourOffset, [ await R.exec("DELETE FROM heartbeat WHERE time < " + sqlHourOffset, [parsedPeriod * -24]);
parsedPeriod * -24,
]);
let timestamp = dayjs().subtract(parsedPeriod, "day").utc().startOf("day").unix(); let timestamp = dayjs().subtract(parsedPeriod, "day").utc().startOf("day").unix();
// stat_daily // stat_daily
await R.exec("DELETE FROM stat_daily WHERE timestamp < ? ", [ await R.exec("DELETE FROM stat_daily WHERE timestamp < ? ", [timestamp]);
timestamp,
]);
if (Database.dbConfig.type === "sqlite") { if (Database.dbConfig.type === "sqlite") {
await R.exec("PRAGMA optimize;"); await R.exec("PRAGMA optimize;");

View File

@ -7,10 +7,12 @@ const { Notification } = require("../notification");
const { default: NodeFetchCache, MemoryCache } = require("node-fetch-cache"); const { default: NodeFetchCache, MemoryCache } = require("node-fetch-cache");
const TranslatableError = require("../translatable-error"); const TranslatableError = require("../translatable-error");
const cachedFetch = process.env.NODE_ENV ? NodeFetchCache.create({ const cachedFetch = process.env.NODE_ENV
// cache for 8h ? NodeFetchCache.create({
cache: new MemoryCache({ ttl: 1000 * 60 * 60 * 8 }) // cache for 8h
}) : fetch; cache: new MemoryCache({ ttl: 1000 * 60 * 60 * 8 }),
})
: fetch;
/** /**
* Find the RDAP server for a given TLD * Find the RDAP server for a given TLD
@ -28,7 +30,7 @@ async function getRdapServer(tld) {
} }
for (const service of rdapList["services"]) { for (const service of rdapList["services"]) {
const [ tlds, urls ] = service; const [tlds, urls] = service;
if (tlds.includes(tld)) { if (tlds.includes(tld)) {
return urls[0]; return urls[0];
} }
@ -108,7 +110,7 @@ class DomainExpiry extends BeanModel {
* @returns {Promise<DomainExpiry>} Domain bean * @returns {Promise<DomainExpiry>} Domain bean
*/ */
static async findByName(domain) { static async findByName(domain) {
return R.findOne("domain_expiry", "domain = ?", [ domain ]); return R.findOne("domain_expiry", "domain = ?", [domain]);
} }
/** /**
@ -159,7 +161,7 @@ class DomainExpiry extends BeanModel {
if (!tld.domain) { if (!tld.domain) {
throw new TranslatableError("domain_expiry_unsupported_invalid_domain", { hostname: tld.hostname }); throw new TranslatableError("domain_expiry_unsupported_invalid_domain", { hostname: tld.hostname });
} }
if ( !tld.publicSuffix) { if (!tld.publicSuffix) {
throw new TranslatableError("domain_expiry_unsupported_public_suffix", { publicSuffix: tld.publicSuffix }); throw new TranslatableError("domain_expiry_unsupported_public_suffix", { publicSuffix: tld.publicSuffix });
} }
if (tld.isIp) { if (tld.isIp) {
@ -176,9 +178,14 @@ class DomainExpiry extends BeanModel {
// Only warn when the monitor actually has domain expiry notifications enabled. // Only warn when the monitor actually has domain expiry notifications enabled.
// The edit monitor page calls this method frequently while the user is typing. // The edit monitor page calls this method frequently while the user is typing.
if (Boolean(monitor.domainExpiryNotification)) { if (Boolean(monitor.domainExpiryNotification)) {
log.warn("domain_expiry", `Domain expiry unsupported for '.${tld.publicSuffix}' because its RDAP endpoint is not listed in the IANA database.`); log.warn(
"domain_expiry",
`Domain expiry unsupported for '.${tld.publicSuffix}' because its RDAP endpoint is not listed in the IANA database.`
);
} }
throw new TranslatableError("domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint", { publicSuffix: tld.publicSuffix }); throw new TranslatableError("domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint", {
publicSuffix: tld.publicSuffix,
});
} }
return { return {
@ -258,7 +265,10 @@ class DomainExpiry extends BeanModel {
} }
// sanity check if expiry date is valid before calculating days remaining. Should not happen and likely indicates a bug in the code. // sanity check if expiry date is valid before calculating days remaining. Should not happen and likely indicates a bug in the code.
if (!domain.expiry || isNaN(new Date(domain.expiry).getTime())) { if (!domain.expiry || isNaN(new Date(domain.expiry).getTime())) {
log.warn("domain_expiry", `No valid expiry date passed to sendNotifications for ${domainName} (expiry: ${domain.expiry}), skipping notification`); log.warn(
"domain_expiry",
`No valid expiry date passed to sendNotifications for ${domainName} (expiry: ${domain.expiry}), skipping notification`
);
return; return;
} }
@ -269,8 +279,8 @@ class DomainExpiry extends BeanModel {
let notifyDays = await setting("domainExpiryNotifyDays"); let notifyDays = await setting("domainExpiryNotifyDays");
if (notifyDays == null || !Array.isArray(notifyDays)) { if (notifyDays == null || !Array.isArray(notifyDays)) {
// Reset Default // Reset Default
await setSetting("domainExpiryNotifyDays", [ 7, 14, 21 ], "general"); await setSetting("domainExpiryNotifyDays", [7, 14, 21], "general");
notifyDays = [ 7, 14, 21 ]; notifyDays = [7, 14, 21];
} }
if (Array.isArray(notifyDays)) { if (Array.isArray(notifyDays)) {
// Asc sort to avoid sending multiple notifications if daysRemaining is below multiple targetDays // Asc sort to avoid sending multiple notifications if daysRemaining is below multiple targetDays

View File

@ -2,7 +2,6 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
const { R } = require("redbean-node"); const { R } = require("redbean-node");
class Group extends BeanModel { class Group extends BeanModel {
/** /**
* Return an object that ready to parse to JSON for public Only show * Return an object that ready to parse to JSON for public Only show
* necessary data to public * necessary data to public
@ -32,14 +31,18 @@ class Group extends BeanModel {
* @returns {Promise<Bean[]>} List of monitors * @returns {Promise<Bean[]>} List of monitors
*/ */
async getMonitorList() { async getMonitorList() {
return R.convertToBeans("monitor", await R.getAll(` return R.convertToBeans(
"monitor",
await R.getAll(
`
SELECT monitor.*, monitor_group.send_url, monitor_group.custom_url FROM monitor, monitor_group SELECT monitor.*, monitor_group.send_url, monitor_group.custom_url FROM monitor, monitor_group
WHERE monitor.id = monitor_group.monitor_id WHERE monitor.id = monitor_group.monitor_id
AND group_id = ? AND group_id = ?
ORDER BY monitor_group.weight ORDER BY monitor_group.weight
`, [ `,
this.id, [this.id]
])); )
);
} }
} }

View File

@ -8,7 +8,6 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
* 3 = MAINTENANCE * 3 = MAINTENANCE
*/ */
class Heartbeat extends BeanModel { class Heartbeat extends BeanModel {
/** /**
* Return an object that ready to parse to JSON for public * Return an object that ready to parse to JSON for public
* Only show necessary data to public * Only show necessary data to public
@ -18,7 +17,7 @@ class Heartbeat extends BeanModel {
return { return {
status: this.status, status: this.status,
time: this.time, time: this.time,
msg: "", // Hide for public msg: "", // Hide for public
ping: this.ping, ping: this.ping,
}; };
} }
@ -39,7 +38,6 @@ class Heartbeat extends BeanModel {
retries: this._retries, retries: this._retries,
}; };
} }
} }
module.exports = Heartbeat; module.exports = Heartbeat;

View File

@ -1,7 +1,6 @@
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
class Incident extends BeanModel { class Incident extends BeanModel {
/** /**
* Return an object that ready to parse to JSON for public * Return an object that ready to parse to JSON for public
* Only show necessary data to public * Only show necessary data to public

View File

@ -7,14 +7,12 @@ const { UptimeKumaServer } = require("../uptime-kuma-server");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
class Maintenance extends BeanModel { class Maintenance extends BeanModel {
/** /**
* Return an object that ready to parse to JSON for public * Return an object that ready to parse to JSON for public
* Only show necessary data to public * Only show necessary data to public
* @returns {Promise<object>} Object ready to parse * @returns {Promise<object>} Object ready to parse
*/ */
async toPublicJSON() { async toPublicJSON() {
let dateRange = []; let dateRange = [];
if (this.start_date) { if (this.start_date) {
dateRange.push(this.start_date); dateRange.push(this.start_date);
@ -41,14 +39,14 @@ class Maintenance extends BeanModel {
active: !!this.active, active: !!this.active,
dateRange: dateRange, dateRange: dateRange,
timeRange: timeRange, timeRange: timeRange,
weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [], weekdays: this.weekdays ? JSON.parse(this.weekdays) : [],
daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [], daysOfMonth: this.days_of_month ? JSON.parse(this.days_of_month) : [],
timeslotList: [], timeslotList: [],
cron: this.cron, cron: this.cron,
duration: this.duration, duration: this.duration,
durationMinutes: parseInt(this.duration / 60), durationMinutes: parseInt(this.duration / 60),
timezone: await this.getTimezone(), // Only valid timezone timezone: await this.getTimezone(), // Only valid timezone
timezoneOption: this.timezone, // Mainly for dropdown menu, because there is a option "SAME_AS_SERVER" timezoneOption: this.timezone, // Mainly for dropdown menu, because there is a option "SAME_AS_SERVER"
timezoneOffset: await this.getTimezoneOffset(), timezoneOffset: await this.getTimezoneOffset(),
status: await this.getStatus(), status: await this.getStatus(),
}; };
@ -202,7 +200,7 @@ class Maintenance extends BeanModel {
* @returns {void} * @returns {void}
*/ */
static validateCron(cron) { static validateCron(cron) {
let job = new Cron(cron, () => { }); let job = new Cron(cron, () => {});
job.stop(); job.stop();
} }
@ -270,7 +268,7 @@ class Maintenance extends BeanModel {
if (this.strategy === "recurring-interval") { if (this.strategy === "recurring-interval") {
// For recurring-interval, Croner needs to have interval and startAt // For recurring-interval, Croner needs to have interval and startAt
const startDate = dayjs(this.startDate); const startDate = dayjs(this.startDate);
const [ hour, minute ] = this.startTime.split(":"); const [hour, minute] = this.startTime.split(":");
const startDateTime = startDate.hour(hour).minute(minute); const startDateTime = startDate.hour(hour).minute(minute);
// Fix #6118, since the startDateTime is optional, it will throw error if the date is null when using toISOString() // Fix #6118, since the startDateTime is optional, it will throw error if the date is null when using toISOString()
@ -279,31 +277,44 @@ class Maintenance extends BeanModel {
startAt = startDateTime.toISOString(); startAt = startDateTime.toISOString();
} catch (_) {} } catch (_) {}
this.beanMeta.job = new Cron(this.cron, { this.beanMeta.job = new Cron(
timezone: await this.getTimezone(), this.cron,
startAt, {
}, () => { timezone: await this.getTimezone(),
if (!this.lastStartDate || this.interval_day === 1) { startAt,
},
() => {
if (!this.lastStartDate || this.interval_day === 1) {
return startEvent();
}
// If last start date is set, it means the maintenance has been started before
let lastStartDate = dayjs(this.lastStartDate).subtract(1.1, "hour"); // Subtract 1.1 hour to avoid issues with timezone differences
// Check if the interval is enough
if (current.diff(lastStartDate, "day") < this.interval_day) {
log.debug(
"maintenance",
"Maintenance id: " + this.id + " is still in the window, skipping start event"
);
return;
}
log.debug(
"maintenance",
"Maintenance id: " + this.id + " is not in the window, starting event"
);
return startEvent(); return startEvent();
} }
);
// If last start date is set, it means the maintenance has been started before
let lastStartDate = dayjs(this.lastStartDate)
.subtract(1.1, "hour"); // Subtract 1.1 hour to avoid issues with timezone differences
// Check if the interval is enough
if (current.diff(lastStartDate, "day") < this.interval_day) {
log.debug("maintenance", "Maintenance id: " + this.id + " is still in the window, skipping start event");
return;
}
log.debug("maintenance", "Maintenance id: " + this.id + " is not in the window, starting event");
return startEvent();
});
} else { } else {
this.beanMeta.job = new Cron(this.cron, { this.beanMeta.job = new Cron(
timezone: await this.getTimezone(), this.cron,
}, startEvent); {
timezone: await this.getTimezone(),
},
startEvent
);
} }
// Continue if the maintenance is still in the window // Continue if the maintenance is still in the window
@ -314,7 +325,6 @@ class Maintenance extends BeanModel {
log.debug("maintenance", "Maintenance id: " + this.id + " Remaining duration: " + duration + "ms"); log.debug("maintenance", "Maintenance id: " + this.id + " Remaining duration: " + duration + "ms");
startEvent(duration); startEvent(duration);
} }
} catch (e) { } catch (e) {
log.error("maintenance", "Error in maintenance id: " + this.id); log.error("maintenance", "Error in maintenance id: " + this.id);
log.error("maintenance", "Cron: " + this.cron); log.error("maintenance", "Cron: " + this.cron);
@ -324,7 +334,6 @@ class Maintenance extends BeanModel {
throw e; throw e;
} }
} }
} else { } else {
log.error("maintenance", "Maintenance id: " + this.id + " has no cron"); log.error("maintenance", "Maintenance id: " + this.id + " has no cron");
} }
@ -486,12 +495,11 @@ class Maintenance extends BeanModel {
} }
// Remove duplicate // Remove duplicate
dayList = [ ...new Set(dayList) ]; dayList = [...new Set(dayList)];
this.cron = minute + " " + hour + " " + dayList.join(",") + " * *"; this.cron = minute + " " + hour + " " + dayList.join(",") + " * *";
this.duration = this.calcDuration(); this.duration = this.calcDuration();
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -9,15 +9,22 @@ const { Feed } = require("feed");
const config = require("../config"); const config = require("../config");
const { setting } = require("../util-server"); const { setting } = require("../util-server");
const { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_MAINTENANCE, STATUS_PAGE_PARTIAL_DOWN, UP, MAINTENANCE, DOWN } = require("../../src/util"); const {
STATUS_PAGE_ALL_DOWN,
STATUS_PAGE_ALL_UP,
STATUS_PAGE_MAINTENANCE,
STATUS_PAGE_PARTIAL_DOWN,
UP,
MAINTENANCE,
DOWN,
} = require("../../src/util");
class StatusPage extends BeanModel { class StatusPage extends BeanModel {
/** /**
* Like this: { "test-uptime.kuma.pet": "default" } * Like this: { "test-uptime.kuma.pet": "default" }
* @type {{}} * @type {{}}
*/ */
static domainMappingList = { }; static domainMappingList = {};
/** /**
* Handle responses to RSS pages * Handle responses to RSS pages
@ -27,9 +34,7 @@ class StatusPage extends BeanModel {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async handleStatusPageRSSResponse(response, slug, request) { static async handleStatusPageRSSResponse(response, slug, request) {
let statusPage = await R.findOne("status_page", " slug = ? ", [ let statusPage = await R.findOne("status_page", " slug = ? ", [slug]);
slug
]);
if (statusPage) { if (statusPage) {
const feedUrl = await StatusPage.buildRSSUrl(slug, request); const feedUrl = await StatusPage.buildRSSUrl(slug, request);
@ -54,9 +59,7 @@ class StatusPage extends BeanModel {
slug = "default"; slug = "default";
} }
let statusPage = await R.findOne("status_page", " slug = ? ", [ let statusPage = await R.findOne("status_page", " slug = ? ", [slug]);
slug
]);
if (statusPage) { if (statusPage) {
response.send(await StatusPage.renderHTML(indexHTML, statusPage)); response.send(await StatusPage.renderHTML(indexHTML, statusPage));
@ -90,7 +93,7 @@ class StatusPage extends BeanModel {
updated: new Date(), // optional, default = today updated: new Date(), // optional, default = today
}); });
heartbeats.forEach(heartbeat => { heartbeats.forEach((heartbeat) => {
feed.addItem({ feed.addItem({
title: `${heartbeat.name} is down`, title: `${heartbeat.name} is down`,
description: `${heartbeat.name} has been down since ${heartbeat.time} UTC`, description: `${heartbeat.name} has been down since ${heartbeat.time} UTC`,
@ -153,9 +156,7 @@ class StatusPage extends BeanModel {
$("meta[name=description]").attr("content", description155); $("meta[name=description]").attr("content", description155);
if (statusPage.icon) { if (statusPage.icon) {
$("link[rel=icon]") $("link[rel=icon]").attr("href", statusPage.icon).removeAttr("type");
.attr("href", statusPage.icon)
.removeAttr("type");
$("link[rel=apple-touch-icon]").remove(); $("link[rel=apple-touch-icon]").remove();
} }
@ -168,19 +169,19 @@ class StatusPage extends BeanModel {
} }
// OG Meta Tags // OG Meta Tags
let ogTitle = $("<meta property=\"og:title\" content=\"\" />").attr("content", statusPage.title); let ogTitle = $('<meta property="og:title" content="" />').attr("content", statusPage.title);
head.append(ogTitle); head.append(ogTitle);
let ogDescription = $("<meta property=\"og:description\" content=\"\" />").attr("content", description155); let ogDescription = $('<meta property="og:description" content="" />').attr("content", description155);
head.append(ogDescription); head.append(ogDescription);
let ogType = $("<meta property=\"og:type\" content=\"website\" />"); let ogType = $('<meta property="og:type" content="website" />');
head.append(ogType); head.append(ogType);
// Preload data // Preload data
// Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186 // Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186
const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), { const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), {
"isScriptContext": true isScriptContext: true,
}); });
const script = $(` const script = $(`
@ -219,7 +220,7 @@ class StatusPage extends BeanModel {
} }
} }
if (! hasUp) { if (!hasUp) {
status = STATUS_PAGE_ALL_DOWN; status = STATUS_PAGE_ALL_DOWN;
} }
@ -267,21 +268,19 @@ class StatusPage extends BeanModel {
// Public Group List // Public Group List
const showTags = !!statusPage.show_tags; const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [ const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [statusPage.id]);
statusPage.id
]);
let heartbeats = []; let heartbeats = [];
for (let groupBean of list) { for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry); let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry);
for (const monitor of monitorGroup.monitorList) { for (const monitor of monitorGroup.monitorList) {
const heartbeat = await R.findOne("heartbeat", "monitor_id = ? ORDER BY time DESC", [ monitor.id ]); const heartbeat = await R.findOne("heartbeat", "monitor_id = ? ORDER BY time DESC", [monitor.id]);
if (heartbeat) { if (heartbeat) {
heartbeats.push({ heartbeats.push({
...monitor, ...monitor,
status: heartbeat.status, status: heartbeat.status,
time: heartbeat.time time: heartbeat.time,
}); });
} }
} }
@ -292,11 +291,11 @@ class StatusPage extends BeanModel {
let statusDescription = StatusPage.getStatusDescription(status); let statusDescription = StatusPage.getStatusDescription(status);
// keep only DOWN heartbeats in the RSS feed // keep only DOWN heartbeats in the RSS feed
heartbeats = heartbeats.filter(heartbeat => heartbeat.status === DOWN); heartbeats = heartbeats.filter((heartbeat) => heartbeat.status === DOWN);
return { return {
heartbeats, heartbeats,
statusDescription statusDescription,
}; };
} }
@ -309,9 +308,7 @@ class StatusPage extends BeanModel {
const config = await statusPage.toPublicJSON(); const config = await statusPage.toPublicJSON();
// Incident // Incident
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [ let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [statusPage.id]);
statusPage.id,
]);
if (incident) { if (incident) {
incident = incident.toPublicJSON(); incident = incident.toPublicJSON();
@ -323,9 +320,7 @@ class StatusPage extends BeanModel {
const publicGroupList = []; const publicGroupList = [];
const showTags = !!statusPage.show_tags; const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [ const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [statusPage.id]);
statusPage.id
]);
for (let groupBean of list) { for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry); let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry);
@ -379,16 +374,13 @@ class StatusPage extends BeanModel {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async updateDomainNameList(domainNameList) { async updateDomainNameList(domainNameList) {
if (!Array.isArray(domainNameList)) { if (!Array.isArray(domainNameList)) {
throw new Error("Invalid array"); throw new Error("Invalid array");
} }
let trx = await R.begin(); let trx = await R.begin();
await trx.exec("DELETE FROM status_page_cname WHERE status_page_id = ?", [ await trx.exec("DELETE FROM status_page_cname WHERE status_page_id = ?", [this.id]);
this.id,
]);
try { try {
for (let domain of domainNameList) { for (let domain of domainNameList) {
@ -401,9 +393,7 @@ class StatusPage extends BeanModel {
} }
// If the domain name is used in another status page, delete it // If the domain name is used in another status page, delete it
await trx.exec("DELETE FROM status_page_cname WHERE domain = ?", [ await trx.exec("DELETE FROM status_page_cname WHERE domain = ?", [domain]);
domain,
]);
let mapping = trx.dispense("status_page_cname"); let mapping = trx.dispense("status_page_cname");
mapping.status_page_id = this.id; mapping.status_page_id = this.id;
@ -494,9 +484,7 @@ class StatusPage extends BeanModel {
* @returns {Promise<number>} ID of status page * @returns {Promise<number>} ID of status page
*/ */
static async slugToID(slug) { static async slugToID(slug) {
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [ return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [slug]);
slug
]);
} }
/** /**
@ -520,21 +508,23 @@ class StatusPage extends BeanModel {
try { try {
const publicMaintenanceList = []; const publicMaintenanceList = [];
let maintenanceIDList = await R.getCol(` let maintenanceIDList = await R.getCol(
`
SELECT DISTINCT maintenance_id SELECT DISTINCT maintenance_id
FROM maintenance_status_page FROM maintenance_status_page
WHERE status_page_id = ? WHERE status_page_id = ?
`, [ statusPageId ]); `,
[statusPageId]
);
for (const maintenanceID of maintenanceIDList) { for (const maintenanceID of maintenanceIDList) {
let maintenance = UptimeKumaServer.getInstance().getMaintenance(maintenanceID); let maintenance = UptimeKumaServer.getInstance().getMaintenance(maintenanceID);
if (maintenance && await maintenance.isUnderMaintenance()) { if (maintenance && (await maintenance.isUnderMaintenance())) {
publicMaintenanceList.push(await maintenance.toPublicJSON()); publicMaintenanceList.push(await maintenance.toPublicJSON());
} }
} }
return publicMaintenanceList; return publicMaintenanceList;
} catch (error) { } catch (error) {
return []; return [];
} }

View File

@ -1,7 +1,6 @@
const { BeanModel } = require("redbean-node/dist/bean-model"); const { BeanModel } = require("redbean-node/dist/bean-model");
class Tag extends BeanModel { class Tag extends BeanModel {
/** /**
* Return an object that ready to parse to JSON * Return an object that ready to parse to JSON
* @returns {object} Object ready to parse * @returns {object} Object ready to parse

View File

@ -15,7 +15,7 @@ class User extends BeanModel {
static async resetPassword(userID, newPassword) { static async resetPassword(userID, newPassword) {
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [
await passwordHash.generate(newPassword), await passwordHash.generate(newPassword),
userID userID,
]); ]);
} }
@ -27,10 +27,7 @@ class User extends BeanModel {
async resetPassword(newPassword) { async resetPassword(newPassword) {
const hashedPassword = await passwordHash.generate(newPassword); const hashedPassword = await passwordHash.generate(newPassword);
await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [hashedPassword, this.id]);
hashedPassword,
this.id
]);
this.password = hashedPassword; this.password = hashedPassword;
} }
@ -42,12 +39,14 @@ class User extends BeanModel {
* @returns {string} the JsonWebToken as a string * @returns {string} the JsonWebToken as a string
*/ */
static createJWT(user, jwtSecret) { static createJWT(user, jwtSecret) {
return jwt.sign({ return jwt.sign(
username: user.username, {
h: shake256(user.password, SHAKE256_LENGTH), username: user.username,
}, jwtSecret); h: shake256(user.password, SHAKE256_LENGTH),
},
jwtSecret
);
} }
} }
module.exports = User; module.exports = User;

View File

@ -146,7 +146,7 @@ function ApiCache() {
let groupName = req.apicacheGroup; let groupName = req.apicacheGroup;
if (groupName) { if (groupName) {
debug("group detected \"" + groupName + "\""); debug('group detected "' + groupName + '"');
let group = (index.groups[groupName] = index.groups[groupName] || []); let group = (index.groups[groupName] = index.groups[groupName] || []);
group.unshift(key); group.unshift(key);
} }
@ -219,9 +219,12 @@ function ApiCache() {
} }
// add automatic cache clearing from duration, includes max limit on setTimeout // add automatic cache clearing from duration, includes max limit on setTimeout
timers[key] = setTimeout(function () { timers[key] = setTimeout(
instance.clear(key, true); function () {
}, Math.min(duration, 2147483647)); instance.clear(key, true);
},
Math.min(duration, 2147483647)
);
} }
/** /**
@ -246,10 +249,7 @@ function ApiCache() {
oldContent = !Buffer.alloc ? new Buffer(0) : Buffer.alloc(0); oldContent = !Buffer.alloc ? new Buffer(0) : Buffer.alloc(0);
} }
res._apicache.content = Buffer.concat( res._apicache.content = Buffer.concat([oldContent, content], oldContent.length + content.length);
[oldContent, content],
oldContent.length + content.length
);
} else { } else {
res._apicache.content = content; res._apicache.content = content;
} }
@ -268,7 +268,7 @@ function ApiCache() {
* @param {function(Object, Object):boolean} toggle * @param {function(Object, Object):boolean} toggle
*/ */
function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) { function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) {
// monkeypatch res.end to create cache object // monkeypatch res.end to create cache object
res._apicache = { res._apicache = {
write: res.write, write: res.write,
writeHead: res.writeHead, writeHead: res.writeHead,
@ -310,17 +310,12 @@ function ApiCache() {
if (res._apicache.cacheable && res._apicache.content) { if (res._apicache.cacheable && res._apicache.content) {
addIndexEntries(key, req); addIndexEntries(key, req);
let headers = res._apicache.headers || getSafeHeaders(res); let headers = res._apicache.headers || getSafeHeaders(res);
let cacheObject = createCacheObject( let cacheObject = createCacheObject(res.statusCode, headers, res._apicache.content, encoding);
res.statusCode,
headers,
res._apicache.content,
encoding
);
cacheResponse(key, cacheObject, duration); cacheResponse(key, cacheObject, duration);
// display log entry // display log entry
let elapsed = new Date() - req.apicacheTimer; let elapsed = new Date() - req.apicacheTimer;
debug("adding cache entry for \"" + key + "\" @ " + strDuration, logDuration(elapsed)); debug('adding cache entry for "' + key + '" @ ' + strDuration, logDuration(elapsed));
debug("_apicache.headers: ", res._apicache.headers); debug("_apicache.headers: ", res._apicache.headers);
debug("res.getHeaders(): ", getSafeHeaders(res)); debug("res.getHeaders(): ", getSafeHeaders(res));
debug("cacheObject: ", cacheObject); debug("cacheObject: ", cacheObject);
@ -366,8 +361,7 @@ function ApiCache() {
// unstringify buffers // unstringify buffers
let data = cacheObject.data; let data = cacheObject.data;
if (data && data.type === "Buffer") { if (data && data.type === "Buffer") {
data = data = typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data);
typeof data.data === "number" ? new Buffer.alloc(data.data) : new Buffer.from(data.data);
} }
// test Etag against If-None-Match for 304 // test Etag against If-None-Match for 304
@ -402,10 +396,10 @@ function ApiCache() {
let redis = globalOptions.redisClient; let redis = globalOptions.redisClient;
if (group) { if (group) {
debug("clearing group \"" + target + "\""); debug('clearing group "' + target + '"');
group.forEach(function (key) { group.forEach(function (key) {
debug("clearing cached entry for \"" + key + "\""); debug('clearing cached entry for "' + key + '"');
clearTimeout(timers[key]); clearTimeout(timers[key]);
delete timers[key]; delete timers[key];
if (!globalOptions.redisClient) { if (!globalOptions.redisClient) {
@ -414,7 +408,7 @@ function ApiCache() {
try { try {
redis.del(key); redis.del(key);
} catch (err) { } catch (err) {
console.log("[apicache] error in redis.del(\"" + key + "\")"); console.log('[apicache] error in redis.del("' + key + '")');
} }
} }
index.all = index.all.filter(doesntMatch(key)); index.all = index.all.filter(doesntMatch(key));
@ -422,7 +416,7 @@ function ApiCache() {
delete index.groups[target]; delete index.groups[target];
} else if (target) { } else if (target) {
debug("clearing " + (isAutomatic ? "expired" : "cached") + " entry for \"" + target + "\""); debug("clearing " + (isAutomatic ? "expired" : "cached") + ' entry for "' + target + '"');
clearTimeout(timers[target]); clearTimeout(timers[target]);
delete timers[target]; delete timers[target];
// clear actual cached entry // clear actual cached entry
@ -432,7 +426,7 @@ function ApiCache() {
try { try {
redis.del(target); redis.del(target);
} catch (err) { } catch (err) {
console.log("[apicache] error in redis.del(\"" + target + "\")"); console.log('[apicache] error in redis.del("' + target + '")');
} }
} }
@ -461,7 +455,7 @@ function ApiCache() {
try { try {
redis.del(key); redis.del(key);
} catch (err) { } catch (err) {
console.log("[apicache] error in redis.del(\"" + key + "\")"); console.log('[apicache] error in redis.del("' + key + '")');
} }
}); });
} }
@ -511,15 +505,15 @@ function ApiCache() {
}; };
/** /**
* Return cache performance statistics (hit rate). Suitable for * Return cache performance statistics (hit rate). Suitable for
* putting into a route: * putting into a route:
* <code> * <code>
* app.get('/api/cache/performance', (req, res) => { * app.get('/api/cache/performance', (req, res) => {
* res.json(apicache.getPerformance()) * res.json(apicache.getPerformance())
* }) * })
* </code> * </code>
* @returns {any[]} * @returns {any[]}
*/ */
this.getPerformance = function () { this.getPerformance = function () {
return performanceArray.map(function (p) { return performanceArray.map(function (p) {
return p.report(); return p.report();
@ -762,8 +756,8 @@ function ApiCache() {
} }
if ( if (
req.headers["x-apicache-bypass"] || req.headers["x-apicache-bypass"] ||
req.headers["x-apicache-force-fetch"] || req.headers["x-apicache-force-fetch"] ||
(opt.respectCacheControl && req.headers["cache-control"] == "no-cache") (opt.respectCacheControl && req.headers["cache-control"] == "no-cache")
) { ) {
return bypass(); return bypass();
} }
@ -830,15 +824,7 @@ function ApiCache() {
); );
} else { } else {
perf.miss(key); perf.miss(key);
return makeResponseCacheable( return makeResponseCacheable(req, res, next, key, duration, strDuration, middlewareToggle);
req,
res,
next,
key,
duration,
strDuration,
middlewareToggle
);
} }
}); });
} catch (err) { } catch (err) {

View File

@ -1,9 +1,7 @@
const apicache = require("./apicache"); const apicache = require("./apicache");
apicache.options({ apicache.options({
headerBlacklist: [ headerBlacklist: ["cache-control"],
"cache-control"
],
headers: { headers: {
// Disable client side cache, only server side cache. // Disable client side cache, only server side cache.
// BUG! Not working for the second request // BUG! Not working for the second request

View File

@ -22,7 +22,7 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
timeout: setTimeout(function () { timeout: setTimeout(function () {
instance.delete(key); instance.delete(key);
return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key); return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key);
}, time) }, time),
}; };
this.cache[key] = entry; this.cache[key] = entry;

View File

@ -1,4 +1,4 @@
'use strict'; "use strict";
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js // Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js
module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0; module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0;
/* Indicates that Unicode strings are supported for use in security buffer /* Indicates that Unicode strings are supported for use in security buffer

View File

@ -1,8 +1,9 @@
'use strict'; "use strict";
// Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js // Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js
var crypto = require('crypto'); var crypto = require("crypto");
function createLMResponse(challenge, lmhash) { function createLMResponse(challenge, lmhash) {
var buf = new Buffer.alloc(24), pwBuffer = new Buffer.alloc(21).fill(0); var buf = new Buffer.alloc(24),
pwBuffer = new Buffer.alloc(21).fill(0);
lmhash.copy(pwBuffer); lmhash.copy(pwBuffer);
calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf); calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf);
calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8); calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8);
@ -10,40 +11,40 @@ function createLMResponse(challenge, lmhash) {
return buf; return buf;
} }
function createLMHash(password) { function createLMHash(password) {
var buf = new Buffer.alloc(16), pwBuffer = new Buffer.alloc(14), magicKey = new Buffer.from('KGS!@#$%', 'ascii'); var buf = new Buffer.alloc(16),
pwBuffer = new Buffer.alloc(14),
magicKey = new Buffer.from("KGS!@#$%", "ascii");
if (password.length > 14) { if (password.length > 14) {
buf.fill(0); buf.fill(0);
return buf; return buf;
} }
pwBuffer.fill(0); pwBuffer.fill(0);
pwBuffer.write(password.toUpperCase(), 0, 'ascii'); pwBuffer.write(password.toUpperCase(), 0, "ascii");
return Buffer.concat([ return Buffer.concat([calculateDES(pwBuffer.slice(0, 7), magicKey), calculateDES(pwBuffer.slice(7), magicKey)]);
calculateDES(pwBuffer.slice(0, 7), magicKey),
calculateDES(pwBuffer.slice(7), magicKey)
]);
} }
function calculateDES(key, message) { function calculateDES(key, message) {
var desKey = new Buffer.alloc(8); var desKey = new Buffer.alloc(8);
desKey[0] = key[0] & 0xFE; desKey[0] = key[0] & 0xfe;
desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1); desKey[1] = ((key[0] << 7) & 0xff) | (key[1] >> 1);
desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2); desKey[2] = ((key[1] << 6) & 0xff) | (key[2] >> 2);
desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3); desKey[3] = ((key[2] << 5) & 0xff) | (key[3] >> 3);
desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4); desKey[4] = ((key[3] << 4) & 0xff) | (key[4] >> 4);
desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5); desKey[5] = ((key[4] << 3) & 0xff) | (key[5] >> 5);
desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6); desKey[6] = ((key[5] << 2) & 0xff) | (key[6] >> 6);
desKey[7] = (key[6] << 1) & 0xFF; desKey[7] = (key[6] << 1) & 0xff;
for (var i = 0; i < 8; i++) { for (var i = 0; i < 8; i++) {
var parity = 0; var parity = 0;
for (var j = 1; j < 8; j++) { for (var j = 1; j < 8; j++) {
parity += (desKey[i] >> j) % 2; parity += (desKey[i] >> j) % 2;
} }
desKey[i] |= (parity % 2) === 0 ? 1 : 0; desKey[i] |= parity % 2 === 0 ? 1 : 0;
} }
var des = crypto.createCipheriv('DES-ECB', desKey, ''); var des = crypto.createCipheriv("DES-ECB", desKey, "");
return des.update(message); return des.update(message);
} }
function createNTLMResponse(challenge, ntlmhash) { function createNTLMResponse(challenge, ntlmhash) {
var buf = new Buffer.alloc(24), ntlmBuffer = new Buffer.alloc(21).fill(0); var buf = new Buffer.alloc(24),
ntlmBuffer = new Buffer.alloc(21).fill(0);
ntlmhash.copy(ntlmBuffer); ntlmhash.copy(ntlmBuffer);
calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf); calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf);
calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8); calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8);
@ -51,21 +52,23 @@ function createNTLMResponse(challenge, ntlmhash) {
return buf; return buf;
} }
function createNTLMHash(password) { function createNTLMHash(password) {
var md4sum = crypto.createHash('md4'); var md4sum = crypto.createHash("md4");
md4sum.update(new Buffer.from(password, 'ucs2')); md4sum.update(new Buffer.from(password, "ucs2"));
return md4sum.digest(); return md4sum.digest();
} }
function createNTLMv2Hash(ntlmhash, username, authTargetName) { function createNTLMv2Hash(ntlmhash, username, authTargetName) {
var hmac = crypto.createHmac('md5', ntlmhash); var hmac = crypto.createHmac("md5", ntlmhash);
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, 'ucs2')); hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, "ucs2"));
return hmac.digest(); return hmac.digest();
} }
function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) { function createLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
var buf = new Buffer.alloc(24), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash); var buf = new Buffer.alloc(24),
ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName),
hmac = crypto.createHmac("md5", ntlm2hash);
//server challenge //server challenge
type2message.challenge.copy(buf, 8); type2message.challenge.copy(buf, 8);
//client nonce //client nonce
buf.write(nonce || createPseudoRandomValue(16), 16, 'hex'); buf.write(nonce || createPseudoRandomValue(16), 16, "hex");
//create hash //create hash
hmac.update(buf.slice(8)); hmac.update(buf.slice(8));
var hashedBuffer = hmac.digest(); var hashedBuffer = hmac.digest();
@ -73,7 +76,9 @@ function createLMv2Response(type2message, username, ntlmhash, nonce, targetName)
return buf; return buf;
} }
function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) { function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetName) {
var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length), ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName), hmac = crypto.createHmac('md5', ntlm2hash); var buf = new Buffer.alloc(48 + type2message.targetInfo.buffer.length),
ntlm2hash = createNTLMv2Hash(ntlmhash, username, targetName),
hmac = crypto.createHmac("md5", ntlm2hash);
//the first 8 bytes are spare to store the hashed value before the blob //the first 8 bytes are spare to store the hashed value before the blob
//server challenge //server challenge
type2message.challenge.copy(buf, 8); type2message.challenge.copy(buf, 8);
@ -86,12 +91,12 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
// maybe think about a different solution here // maybe think about a different solution here
// 11644473600000 = diff between 1970 and 1601 // 11644473600000 = diff between 1970 and 1601
var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16); var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16);
var timestampLow = Number('0x' + timestamp.substring(Math.max(0, timestamp.length - 8))); var timestampLow = Number("0x" + timestamp.substring(Math.max(0, timestamp.length - 8)));
var timestampHigh = Number('0x' + timestamp.substring(0, Math.max(0, timestamp.length - 8))); var timestampHigh = Number("0x" + timestamp.substring(0, Math.max(0, timestamp.length - 8)));
buf.writeUInt32LE(timestampLow, 24, false); buf.writeUInt32LE(timestampLow, 24, false);
buf.writeUInt32LE(timestampHigh, 28, false); buf.writeUInt32LE(timestampHigh, 28, false);
//random client nonce //random client nonce
buf.write(nonce || createPseudoRandomValue(16), 32, 'hex'); buf.write(nonce || createPseudoRandomValue(16), 32, "hex");
//zero //zero
buf.writeUInt32LE(0, 40); buf.writeUInt32LE(0, 40);
//complete target information block from type 2 message //complete target information block from type 2 message
@ -104,7 +109,7 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
return buf; return buf;
} }
function createPseudoRandomValue(length) { function createPseudoRandomValue(length) {
var str = ''; var str = "";
while (str.length < length) { while (str.length < length) {
str += crypto.randomInt(16).toString(16); str += crypto.randomInt(16).toString(16);
} }
@ -117,6 +122,6 @@ module.exports = {
createNTLMResponse: createNTLMResponse, createNTLMResponse: createNTLMResponse,
createLMv2Response: createLMv2Response, createLMv2Response: createLMv2Response,
createNTLMv2Response: createNTLMv2Response, createNTLMv2Response: createNTLMv2Response,
createPseudoRandomValue: createPseudoRandomValue createPseudoRandomValue: createPseudoRandomValue,
}; };
//# sourceMappingURL=hash.js.map //# sourceMappingURL=hash.js.map

View File

@ -1,23 +1,30 @@
'use strict'; "use strict";
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/ntlm.js // Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/ntlm.js
var os = require('os'), flags = require('./flags'), hash = require('./hash'); var os = require("os"),
flags = require("./flags"),
hash = require("./hash");
var NTLMSIGNATURE = "NTLMSSP\0"; var NTLMSIGNATURE = "NTLMSSP\0";
function createType1Message(workstation, target) { function createType1Message(workstation, target) {
var dataPos = 32, pos = 0, buf = new Buffer.alloc(1024); var dataPos = 32,
pos = 0,
buf = new Buffer.alloc(1024);
workstation = workstation === undefined ? os.hostname() : workstation; workstation = workstation === undefined ? os.hostname() : workstation;
target = target === undefined ? '' : target; target = target === undefined ? "" : target;
//signature //signature
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii'); buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, "ascii");
pos += NTLMSIGNATURE.length; pos += NTLMSIGNATURE.length;
//message type //message type
buf.writeUInt32LE(1, pos); buf.writeUInt32LE(1, pos);
pos += 4; pos += 4;
//flags //flags
buf.writeUInt32LE(flags.NTLMFLAG_NEGOTIATE_OEM | buf.writeUInt32LE(
flags.NTLMFLAG_REQUEST_TARGET | flags.NTLMFLAG_NEGOTIATE_OEM |
flags.NTLMFLAG_NEGOTIATE_NTLM_KEY | flags.NTLMFLAG_REQUEST_TARGET |
flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY | flags.NTLMFLAG_NEGOTIATE_NTLM_KEY |
flags.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN, pos); flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY |
flags.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN,
pos
);
pos += 4; pos += 4;
//domain security buffer //domain security buffer
buf.writeUInt16LE(target.length, pos); buf.writeUInt16LE(target.length, pos);
@ -27,7 +34,7 @@ function createType1Message(workstation, target) {
buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos); buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
pos += 4; pos += 4;
if (target.length > 0) { if (target.length > 0) {
dataPos += buf.write(target, dataPos, 'ascii'); dataPos += buf.write(target, dataPos, "ascii");
} }
//workstation security buffer //workstation security buffer
buf.writeUInt16LE(workstation.length, pos); buf.writeUInt16LE(workstation.length, pos);
@ -37,40 +44,40 @@ function createType1Message(workstation, target) {
buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos); buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
pos += 4; pos += 4;
if (workstation.length > 0) { if (workstation.length > 0) {
dataPos += buf.write(workstation, dataPos, 'ascii'); dataPos += buf.write(workstation, dataPos, "ascii");
} }
return 'NTLM ' + buf.toString('base64', 0, dataPos); return "NTLM " + buf.toString("base64", 0, dataPos);
} }
function decodeType2Message(str) { function decodeType2Message(str) {
if (str === undefined) { if (str === undefined) {
throw new Error('Invalid argument'); throw new Error("Invalid argument");
} }
//convenience //convenience
if (Object.prototype.toString.call(str) !== '[object String]') { if (Object.prototype.toString.call(str) !== "[object String]") {
if (str.hasOwnProperty('headers') && str.headers.hasOwnProperty('www-authenticate')) { if (str.hasOwnProperty("headers") && str.headers.hasOwnProperty("www-authenticate")) {
str = str.headers['www-authenticate']; str = str.headers["www-authenticate"];
} } else {
else { throw new Error("Invalid argument");
throw new Error('Invalid argument');
} }
} }
var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str); var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
if (ntlmMatch) { if (ntlmMatch) {
str = ntlmMatch[1]; str = ntlmMatch[1];
} }
var buf = new Buffer.from(str, 'base64'), obj = {}; var buf = new Buffer.from(str, "base64"),
obj = {};
//check signature //check signature
if (buf.toString('ascii', 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) { if (buf.toString("ascii", 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
throw new Error('Invalid message signature: ' + str); throw new Error("Invalid message signature: " + str);
} }
//check message type //check message type
if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 2) { if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 2) {
throw new Error('Invalid message type (no type 2)'); throw new Error("Invalid message type (no type 2)");
} }
//read flags //read flags
obj.flags = buf.readUInt32LE(20); obj.flags = buf.readUInt32LE(20);
obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? 'ascii' : 'ucs2'; obj.encoding = obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM ? "ascii" : "ucs2";
obj.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1; obj.version = obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY ? 2 : 1;
obj.challenge = buf.slice(24, 32); obj.challenge = buf.slice(24, 32);
//read target name //read target name
obj.targetName = (function () { obj.targetName = (function () {
@ -78,10 +85,10 @@ function decodeType2Message(str) {
//skipping allocated space //skipping allocated space
var offset = buf.readUInt32LE(16); var offset = buf.readUInt32LE(16);
if (length === 0) { if (length === 0) {
return ''; return "";
} }
if ((offset + length) > buf.length || offset < 32) { if (offset + length > buf.length || offset < 32) {
throw new Error('Bad type 2 message'); throw new Error("Bad type 2 message");
} }
return buf.toString(obj.encoding, offset, offset + length); return buf.toString(obj.encoding, offset, offset + length);
})(); })();
@ -97,11 +104,11 @@ function decodeType2Message(str) {
if (length === 0) { if (length === 0) {
return info; return info;
} }
if ((offset + length) > buf.length || offset < 32) { if (offset + length > buf.length || offset < 32) {
throw new Error('Bad type 2 message'); throw new Error("Bad type 2 message");
} }
var pos = offset; var pos = offset;
while (pos < (offset + length)) { while (pos < offset + length) {
var blockType = buf.readUInt16LE(pos); var blockType = buf.readUInt16LE(pos);
pos += 2; pos += 2;
var blockLength = buf.readUInt16LE(pos); var blockLength = buf.readUInt16LE(pos);
@ -113,39 +120,40 @@ function decodeType2Message(str) {
var blockTypeStr = void 0; var blockTypeStr = void 0;
switch (blockType) { switch (blockType) {
case 1: case 1:
blockTypeStr = 'SERVER'; blockTypeStr = "SERVER";
break; break;
case 2: case 2:
blockTypeStr = 'DOMAIN'; blockTypeStr = "DOMAIN";
break; break;
case 3: case 3:
blockTypeStr = 'FQDN'; blockTypeStr = "FQDN";
break; break;
case 4: case 4:
blockTypeStr = 'DNS'; blockTypeStr = "DNS";
break; break;
case 5: case 5:
blockTypeStr = 'PARENT_DNS'; blockTypeStr = "PARENT_DNS";
break; break;
default: default:
blockTypeStr = ''; blockTypeStr = "";
break; break;
} }
if (blockTypeStr) { if (blockTypeStr) {
info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength); info[blockTypeStr] = buf.toString("ucs2", pos, pos + blockLength);
} }
pos += blockLength; pos += blockLength;
} }
return { return {
parsed: info, parsed: info,
buffer: targetInfoBuffer buffer: targetInfoBuffer,
}; };
})(); })();
} }
return obj; return obj;
} }
function createType3Message(type2Message, username, password, workstation, target) { function createType3Message(type2Message, username, password, workstation, target) {
var dataPos = 52, buf = new Buffer.alloc(1024); var dataPos = 52,
buf = new Buffer.alloc(1024);
if (workstation === undefined) { if (workstation === undefined) {
workstation = os.hostname(); workstation = os.hostname();
} }
@ -153,12 +161,15 @@ function createType3Message(type2Message, username, password, workstation, targe
target = type2Message.targetName; target = type2Message.targetName;
} }
//signature //signature
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, 'ascii'); buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, "ascii");
//message type //message type
buf.writeUInt32LE(3, 8); buf.writeUInt32LE(3, 8);
if (type2Message.version === 2) { if (type2Message.version === 2) {
dataPos = 64; dataPos = 64;
var ntlmHash = hash.createNTLMHash(password), nonce = hash.createPseudoRandomValue(16), lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target), ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target); var ntlmHash = hash.createNTLMHash(password),
nonce = hash.createPseudoRandomValue(16),
lmv2 = hash.createLMv2Response(type2Message, username, ntlmHash, nonce, target),
ntlmv2 = hash.createNTLMv2Response(type2Message, username, ntlmHash, nonce, target);
//lmv2 security buffer //lmv2 security buffer
buf.writeUInt16LE(lmv2.length, 12); buf.writeUInt16LE(lmv2.length, 12);
buf.writeUInt16LE(lmv2.length, 14); buf.writeUInt16LE(lmv2.length, 14);
@ -171,9 +182,11 @@ function createType3Message(type2Message, username, password, workstation, targe
buf.writeUInt32LE(dataPos, 24); buf.writeUInt32LE(dataPos, 24);
ntlmv2.copy(buf, dataPos); ntlmv2.copy(buf, dataPos);
dataPos += ntlmv2.length; dataPos += ntlmv2.length;
} } else {
else { var lmHash = hash.createLMHash(password),
var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password), lm = hash.createLMResponse(type2Message.challenge, lmHash), ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash); ntlmHash = hash.createNTLMHash(password),
lm = hash.createLMResponse(type2Message.challenge, lmHash),
ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
//lm security buffer //lm security buffer
buf.writeUInt16LE(lm.length, 12); buf.writeUInt16LE(lm.length, 12);
buf.writeUInt16LE(lm.length, 14); buf.writeUInt16LE(lm.length, 14);
@ -188,18 +201,18 @@ function createType3Message(type2Message, username, password, workstation, targe
dataPos += ntlm.length; dataPos += ntlm.length;
} }
//target name security buffer //target name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 28); buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 28);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? target.length : target.length * 2, 30); buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 30);
buf.writeUInt32LE(dataPos, 32); buf.writeUInt32LE(dataPos, 32);
dataPos += buf.write(target, dataPos, type2Message.encoding); dataPos += buf.write(target, dataPos, type2Message.encoding);
//user name security buffer //user name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 36); buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 36);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? username.length : username.length * 2, 38); buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 38);
buf.writeUInt32LE(dataPos, 40); buf.writeUInt32LE(dataPos, 40);
dataPos += buf.write(username, dataPos, type2Message.encoding); dataPos += buf.write(username, dataPos, type2Message.encoding);
//workstation name security buffer //workstation name security buffer
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 44); buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 44);
buf.writeUInt16LE(type2Message.encoding === 'ascii' ? workstation.length : workstation.length * 2, 46); buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 46);
buf.writeUInt32LE(dataPos, 48); buf.writeUInt32LE(dataPos, 48);
dataPos += buf.write(workstation, dataPos, type2Message.encoding); dataPos += buf.write(workstation, dataPos, type2Message.encoding);
if (type2Message.version === 2) { if (type2Message.version === 2) {
@ -210,11 +223,11 @@ function createType3Message(type2Message, username, password, workstation, targe
//flags //flags
buf.writeUInt32LE(type2Message.flags, 60); buf.writeUInt32LE(type2Message.flags, 60);
} }
return 'NTLM ' + buf.toString('base64', 0, dataPos); return "NTLM " + buf.toString("base64", 0, dataPos);
} }
module.exports = { module.exports = {
createType1Message: createType1Message, createType1Message: createType1Message,
decodeType2Message: decodeType2Message, decodeType2Message: decodeType2Message,
createType3Message: createType3Message createType3Message: createType3Message,
}; };
//# sourceMappingURL=ntlm.js.map //# sourceMappingURL=ntlm.js.map

View File

@ -1,62 +1,176 @@
"use strict"; "use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { var __createBinding =
if (k2 === undefined) k2 = k; (this && this.__createBinding) ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); (Object.create
}) : (function(o, m, k, k2) { ? function (o, m, k, k2) {
if (k2 === undefined) k2 = k; if (k2 === undefined) k2 = k;
o[k2] = m[k]; Object.defineProperty(o, k2, {
})); enumerable: true,
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { get: function () {
Object.defineProperty(o, "default", { enumerable: true, value: v }); return m[k];
}) : function(o, v) { },
o["default"] = v; });
}); }
var __importStar = (this && this.__importStar) || function (mod) { : function (o, m, k, k2) {
if (mod && mod.__esModule) return mod; if (k2 === undefined) k2 = k;
var result = {}; o[k2] = m[k];
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); });
__setModuleDefault(result, mod); var __setModuleDefault =
return result; (this && this.__setModuleDefault) ||
}; (Object.create
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { ? function (o, v) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } Object.defineProperty(o, "default", { enumerable: true, value: v });
return new (P || (P = Promise))(function (resolve, reject) { }
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } : function (o, v) {
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } o["default"] = v;
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } });
step((generator = generator.apply(thisArg, _arguments || [])).next()); var __importStar =
}); (this && this.__importStar) ||
}; function (mod) {
var __generator = (this && this.__generator) || function (thisArg, body) { if (mod && mod.__esModule) return mod;
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; var result = {};
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; if (mod != null)
function verb(n) { return function (v) { return step([n, v]); }; } for (var k in mod)
function step(op) { if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
if (f) throw new TypeError("Generator is already executing."); __setModuleDefault(result, mod);
while (_) try { return result;
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; };
if (y = 0, t) op = [op[0] & 2, t.value]; var __awaiter =
switch (op[0]) { (this && this.__awaiter) ||
case 0: case 1: t = op; break; function (thisArg, _arguments, P, generator) {
case 4: _.label++; return { value: op[1], done: false }; function adopt(value) {
case 5: _.label++; y = op[1]; op = [0]; continue; return value instanceof P
case 7: op = _.ops.pop(); _.trys.pop(); continue; ? value
default: : new P(function (resolve) {
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } resolve(value);
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } });
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } return new (P || (P = Promise))(function (resolve, reject) {
if (t[2]) _.ops.pop(); function fulfilled(value) {
_.trys.pop(); continue; try {
step(generator.next(value));
} catch (e) {
reject(e);
}
} }
op = body.call(thisArg, _); function rejected(value) {
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } try {
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; step(generator["throw"](value));
} } catch (e) {
}; reject(e);
var __importDefault = (this && this.__importDefault) || function (mod) { }
return (mod && mod.__esModule) ? mod : { "default": mod }; }
}; function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator =
(this && this.__generator) ||
function (thisArg, body) {
var _ = {
label: 0,
sent: function () {
if (t[0] & 1) throw t[1];
return t[1];
},
trys: [],
ops: [],
},
f,
y,
t,
g;
return (
(g = { next: verb(0), throw: verb(1), return: verb(2) }),
typeof Symbol === "function" &&
(g[Symbol.iterator] = function () {
return this;
}),
g
);
function verb(n) {
return function (v) {
return step([n, v]);
};
}
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_)
try {
if (
((f = 1),
y &&
(t =
op[0] & 2
? y["return"]
: op[0]
? y["throw"] || ((t = y["return"]) && t.call(y), 0)
: y.next) &&
!(t = t.call(y, op[1])).done)
)
return t;
if (((y = 0), t)) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0:
case 1:
t = op;
break;
case 4:
_.label++;
return { value: op[1], done: false };
case 5:
_.label++;
y = op[1];
op = [0];
continue;
case 7:
op = _.ops.pop();
_.trys.pop();
continue;
default:
if (
!((t = _.trys), (t = t.length > 0 && t[t.length - 1])) &&
(op[0] === 6 || op[0] === 2)
) {
_ = 0;
continue;
}
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
_.label = op[1];
break;
}
if (op[0] === 6 && _.label < t[1]) {
_.label = t[1];
t = op;
break;
}
if (t && _.label < t[2]) {
_.label = t[2];
_.ops.push(op);
break;
}
if (t[2]) _.ops.pop();
_.trys.pop();
continue;
}
op = body.call(thisArg, _);
} catch (e) {
op = [6, e];
y = 0;
} finally {
f = t = 0;
}
if (op[0] & 5) throw op[1];
return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.NtlmClient = void 0; exports.NtlmClient = void 0;
var axios_1 = __importDefault(require("axios")); var axios_1 = __importDefault(require("axios"));
@ -65,11 +179,11 @@ var https = __importStar(require("https"));
var http = __importStar(require("http")); var http = __importStar(require("http"));
var dev_null_1 = __importDefault(require("dev-null")); var dev_null_1 = __importDefault(require("dev-null"));
/** /**
* @param credentials An NtlmCredentials object containing the username and password * @param credentials An NtlmCredentials object containing the username and password
* @param AxiosConfig The Axios config for the instance you wish to create * @param AxiosConfig The Axios config for the instance you wish to create
* *
* @returns This function returns an axios instance configured to use the provided credentials * @returns This function returns an axios instance configured to use the provided credentials
*/ */
function NtlmClient(credentials, AxiosConfig) { function NtlmClient(credentials, AxiosConfig) {
var _this = this; var _this = this;
var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {}; var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
@ -80,50 +194,75 @@ function NtlmClient(credentials, AxiosConfig) {
config.httpsAgent = new https.Agent({ keepAlive: true }); config.httpsAgent = new https.Agent({ keepAlive: true });
} }
var client = axios_1.default.create(config); var client = axios_1.default.create(config);
client.interceptors.response.use(function (response) { client.interceptors.response.use(
return response; function (response) {
}, function (err) { return __awaiter(_this, void 0, void 0, function () { return response;
var error, t1Msg, t2Msg, t3Msg, stream_1; },
var _a; function (err) {
return __generator(this, function (_b) { return __awaiter(_this, void 0, void 0, function () {
switch (_b.label) { var error, t1Msg, t2Msg, t3Msg, stream_1;
case 0: var _a;
error = err.response; return __generator(this, function (_b) {
// The header may look like this: `Negotiate, NTLM, Basic realm="itsahiddenrealm.example.net"`Add commentMore actions switch (_b.label) {
// so extract the 'NTLM' part first case 0:
const ntlmheader = error.headers['www-authenticate'].split(',').find(_ => _.match(/ *NTLM/))?.trim() || ''; error = err.response;
if (!(error && error.status === 401 // The header may look like this: `Negotiate, NTLM, Basic realm="itsahiddenrealm.example.net"`Add commentMore actions
&& error.headers['www-authenticate'] // so extract the 'NTLM' part first
&& error.headers['www-authenticate'].includes('NTLM'))) return [3 /*break*/, 3]; const ntlmheader =
// This length check is a hack because SharePoint is awkward and will error.headers["www-authenticate"]
// include the Negotiate option when responding with the T2 message .split(",")
// There is nore we could do to ensure we are processing correctly, .find((_) => _.match(/ *NTLM/))
// but this is the easiest option for now ?.trim() || "";
if (ntlmheader.length < 50) { if (
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain); !(
error.config.headers["Authorization"] = t1Msg; error &&
error.status === 401 &&
error.headers["www-authenticate"] &&
error.headers["www-authenticate"].includes("NTLM")
)
)
return [3 /*break*/, 3];
// This length check is a hack because SharePoint is awkward and will
// include the Negotiate option when responding with the T2 message
// There is nore we could do to ensure we are processing correctly,
// but this is the easiest option for now
if (ntlmheader.length < 50) {
t1Msg = ntlm.createType1Message(credentials.workstation, credentials.domain);
error.config.headers["Authorization"] = t1Msg;
} else {
t2Msg = ntlm.decodeType2Message((ntlmheader.match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]);
t3Msg = ntlm.createType3Message(
t2Msg,
credentials.username,
credentials.password,
credentials.workstation,
credentials.domain
);
error.config.headers["X-retry"] = "false";
error.config.headers["Authorization"] = t3Msg;
}
if (!(error.config.responseType === "stream")) return [3 /*break*/, 2];
stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
if (!(stream_1 && !stream_1.readableEnded)) return [3 /*break*/, 2];
return [
4 /*yield*/,
new Promise(function (resolve) {
stream_1.pipe((0, dev_null_1.default)());
stream_1.once("close", resolve);
}),
];
case 1:
_b.sent();
_b.label = 2;
case 2:
return [2 /*return*/, client(error.config)];
case 3:
throw err;
} }
else { });
t2Msg = ntlm.decodeType2Message((ntlmheader.match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1]); });
t3Msg = ntlm.createType3Message(t2Msg, credentials.username, credentials.password, credentials.workstation, credentials.domain); }
error.config.headers["X-retry"] = "false"; );
error.config.headers["Authorization"] = t3Msg;
}
if (!(error.config.responseType === "stream")) return [3 /*break*/, 2];
stream_1 = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data;
if (!(stream_1 && !stream_1.readableEnded)) return [3 /*break*/, 2];
return [4 /*yield*/, new Promise(function (resolve) {
stream_1.pipe((0, dev_null_1.default)());
stream_1.once('close', resolve);
})];
case 1:
_b.sent();
_b.label = 2;
case 2: return [2 /*return*/, client(error.config)];
case 3: throw err;
}
});
}); });
return client; return client;
} }
exports.NtlmClient = NtlmClient; exports.NtlmClient = NtlmClient;

View File

@ -1,20 +1,20 @@
import { PluginFunc, ConfigType } from 'dayjs' import { PluginFunc, ConfigType } from "dayjs";
declare const plugin: PluginFunc declare const plugin: PluginFunc;
export = plugin export = plugin;
declare module 'dayjs' { declare module "dayjs" {
interface Dayjs { interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs tz(timezone?: string, keepLocalTime?: boolean): Dayjs;
offsetName(type?: 'short' | 'long'): string | undefined offsetName(type?: "short" | "long"): string | undefined;
} }
interface DayjsTimezone { interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs (date: ConfigType, timezone?: string): Dayjs;
(date: ConfigType, format: string, timezone?: string): Dayjs (date: ConfigType, format: string, timezone?: string): Dayjs;
guess(): string guess(): string;
setDefault(timezone?: string): void setDefault(timezone?: string): void;
} }
const tz: DayjsTimezone const tz: DayjsTimezone;
} }

View File

@ -4,10 +4,14 @@
* Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc * Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc
* License: MIT * License: MIT
*/ */
!function (t, e) { !(function (t, e) {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
typeof exports == "object" && typeof module != "undefined" ? module.exports = e() : typeof define == "function" && define.amd ? define(e) : (t = typeof globalThis != "undefined" ? globalThis : t || self).dayjs_plugin_timezone = e(); typeof exports == "object" && typeof module != "undefined"
}(this, (function () { ? (module.exports = e())
: typeof define == "function" && define.amd
? define(e)
: ((t = typeof globalThis != "undefined" ? globalThis : t || self).dayjs_plugin_timezone = e());
})(this, function () {
"use strict"; "use strict";
let t = { let t = {
year: 0, year: 0,
@ -15,7 +19,7 @@
day: 2, day: 2,
hour: 3, hour: 3,
minute: 4, minute: 4,
second: 5 second: 5,
}; };
let e = {}; let e = {};
return function (n, i, o) { return function (n, i, o) {
@ -23,23 +27,28 @@
let a = function (t, n, i) { let a = function (t, n, i) {
void 0 === i && (i = {}); void 0 === i && (i = {});
let o = new Date(t); let o = new Date(t);
let r = function (t, n) { let r = (function (t, n) {
void 0 === n && (n = {}); void 0 === n && (n = {});
let i = n.timeZoneName || "short"; let i = n.timeZoneName || "short";
let o = t + "|" + i; let o = t + "|" + i;
let r = e[o]; let r = e[o];
return r || (r = new Intl.DateTimeFormat("en-US", { return (
hour12: !1, r ||
timeZone: t, ((r = new Intl.DateTimeFormat("en-US", {
year: "numeric", hour12: !1,
month: "2-digit", timeZone: t,
day: "2-digit", year: "numeric",
hour: "2-digit", month: "2-digit",
minute: "2-digit", day: "2-digit",
second: "2-digit", hour: "2-digit",
timeZoneName: i minute: "2-digit",
}), e[o] = r), r; second: "2-digit",
}(n, i); timeZoneName: i,
})),
(e[o] = r)),
r
);
})(n, i);
return r.formatToParts(o); return r.formatToParts(o);
}; };
let u = function (e, n) { let u = function (e, n) {
@ -60,56 +69,62 @@
return (o.utc(v).valueOf() - (h -= h % 1e3)) / 6e4; return (o.utc(v).valueOf() - (h -= h % 1e3)) / 6e4;
}; };
let f = i.prototype; let f = i.prototype;
f.tz = function (t, e) { ((f.tz = function (t, e) {
void 0 === t && (t = r); void 0 === t && (t = r);
let n = this.utcOffset(); let n = this.utcOffset();
let i = this.toDate(); let i = this.toDate();
let a = i.toLocaleString("en-US", { timeZone: t }).replace("\u202f", " "); let a = i.toLocaleString("en-US", { timeZone: t }).replace("\u202f", " ");
let u = Math.round((i - new Date(a)) / 1e3 / 60); let u = Math.round((i - new Date(a)) / 1e3 / 60);
let f = o(a).$set("millisecond", this.$ms).utcOffset(15 * -Math.round(i.getTimezoneOffset() / 15) - u, !0); let f = o(a)
.$set("millisecond", this.$ms)
.utcOffset(15 * -Math.round(i.getTimezoneOffset() / 15) - u, !0);
if (e) { if (e) {
let s = f.utcOffset(); let s = f.utcOffset();
f = f.add(n - s, "minute"); f = f.add(n - s, "minute");
} }
return f.$x.$timezone = t, f; return ((f.$x.$timezone = t), f);
}, f.offsetName = function (t) { }),
let e = this.$x.$timezone || o.tz.guess(); (f.offsetName = function (t) {
let n = a(this.valueOf(), e, { timeZoneName: t }).find((function (t) { let e = this.$x.$timezone || o.tz.guess();
return t.type.toLowerCase() === "timezonename"; let n = a(this.valueOf(), e, { timeZoneName: t }).find(function (t) {
return t.type.toLowerCase() === "timezonename";
});
return n && n.value;
})); }));
return n && n.value;
};
let s = f.startOf; let s = f.startOf;
f.startOf = function (t, e) { ((f.startOf = function (t, e) {
if (!this.$x || !this.$x.$timezone) { if (!this.$x || !this.$x.$timezone) {
return s.call(this, t, e); return s.call(this, t, e);
} }
let n = o(this.format("YYYY-MM-DD HH:mm:ss:SSS")); let n = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"));
return s.call(n, t, e).tz(this.$x.$timezone, !0); return s.call(n, t, e).tz(this.$x.$timezone, !0);
}, o.tz = function (t, e, n) { }),
let i = n && e; (o.tz = function (t, e, n) {
let a = n || e || r; let i = n && e;
let f = u(+o(), a); let a = n || e || r;
if (typeof t != "string") { let f = u(+o(), a);
return o(t).tz(a); if (typeof t != "string") {
} return o(t).tz(a);
let s = function (t, e, n) {
let i = t - 60 * e * 1e3;
let o = u(i, n);
if (e === o) {
return [ i, e ];
} }
let r = u(i -= 60 * (o - e) * 1e3, n); let s = (function (t, e, n) {
return o === r ? [ i, o ] : [ t - 60 * Math.min(o, r) * 1e3, Math.max(o, r) ]; let i = t - 60 * e * 1e3;
}(o.utc(t, i).valueOf(), f, a); let o = u(i, n);
let m = s[0]; if (e === o) {
let c = s[1]; return [i, e];
let d = o(m).utcOffset(c); }
return d.$x.$timezone = a, d; let r = u((i -= 60 * (o - e) * 1e3), n);
}, o.tz.guess = function () { return o === r ? [i, o] : [t - 60 * Math.min(o, r) * 1e3, Math.max(o, r)];
return Intl.DateTimeFormat().resolvedOptions().timeZone; })(o.utc(t, i).valueOf(), f, a);
}, o.tz.setDefault = function (t) { let m = s[0];
r = t; let c = s[1];
}; let d = o(m).utcOffset(c);
return ((d.$x.$timezone = a), d);
}),
(o.tz.guess = function () {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}),
(o.tz.setDefault = function (t) {
r = t;
}));
}; };
})); });

Some files were not shown because too many files have changed in this diff Show More