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
422 changed files with 30899 additions and 27379 deletions

View File

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

View File

@@ -38,9 +38,8 @@ jobs:
run: npm run lint-fix:style
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
# run: npm run fmt
# continue-on-error: true
- name: Auto-format code with Prettier
run: npm run fmt
continue-on-error: true
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27

View File

@@ -1,38 +1,2 @@
# Dependencies
node_modules/
# 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/
# language files
src/lang/*.json

View File

@@ -54,8 +54,7 @@ to review the appropriate one for your contribution.
[**PLEASE SEE OUR SECURITY POLICY.**](SECURITY.md)
[advisory]: https://github.com/louislam/uptime-kuma/security/advisories/new
[issue]:
https://github.com/louislam/uptime-kuma/issues/new?template=security_issue.yml
[issue]: https://github.com/louislam/uptime-kuma/issues/new?template=security_issue.yml
</p>
</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.
Please make sure that you follow these rules:
- keep the PR as small as possible, fix only one thing at a time => keeping it
reviewable
- test that your code does what you claim it does.
@@ -79,16 +77,15 @@ to review the appropriate one for your contribution.
- <details><summary><b>Translations / Internationalisation (i18n)</b> (click to expand)</summary>
<p>
Please add **all** strings that are translatable to `src/lang/en.json`. If translation keys are omitted, they cannot be translated. **Do not include any other languages in your initial pull request** (even if it is your mother tongue) to avoid merge conflicts between Weblate and `master`. Once your PR is merged into `master`, the strings can be translated by awesome people donating their language skills.
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).
Please add **all** strings that are translatable to `src/lang/en.json`. If translation keys are omitted, they cannot be translated. **Do not include any other languages in your initial pull request** (even if it is your mother tongue) to avoid merge conflicts between Weblate and `master`. Once your PR is merged into `master`, the strings can be translated by awesome people donating their language skills.
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:
- 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.
- **Adding a new language** requires creating a new file. See [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md).
- 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.
- **Adding a new language** requires creating a new file. See [these instructions](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md).
<sub>Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area.</sub>
</p>
@@ -98,7 +95,6 @@ to review the appropriate one for your contribution.
<p>
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
notification provider lives.
@@ -135,7 +131,6 @@ to review the appropriate one for your contribution.
translations (`{{ $t("Translation key") }}`,
[`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
- `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
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
screenshots of the following events in the pull-request description:
- `UP`/`DOWN`
- Certificate Expiry via <https://expired.badssl.com/>
- Domain Expiry via <https://google.com/> and a larger time set
@@ -159,7 +153,7 @@ to review the appropriate one for your contribution.
```md
| Event | Before | After |
|--------------------|-----------------------|----------------------|
| ------------------ | --------------------- | -------------------- |
| `UP` | ![Before](image-link) | ![After](image-link) |
| `DOWN` | ![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>
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.
The `async check(...)`-function should:
- 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>
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.
2. **Create a new branch** for your changes (e.g., `signal-notification-provider`).
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.
Only change the status to **Ready for Review** when:
- You have implemented all planned changes.
- Your code is fully tested and ready for review.
- 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
conflicts with other PRs. Please
- (if possible) **create one PR for one issue** or
- (if not possible) **explain which issues a PR addresses and why this PR
should not be broken apart**
@@ -269,6 +259,7 @@ to review the appropriate one for your contribution.
### Continuous Integration
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`.
- **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`.
@@ -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.
- Easy to install for non-Docker users
- no native build dependency is needed (for `x86_64`/`armv7`/`arm64`)
- no extra configuration and
- no extra effort required to get it running
- Single container for Docker users
- no complex docker-compose file
- 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>
<p>
- amd64, armv7 using local.
- arm64 using remote arm64 cpu, as the emulator is too slow and can no longer
pass the `npm ci` command.
1. Add the public key to the remote server.
2. Add the remote context. The remote machine must be arm64 and installed
Docker CE.
```bash
docker context create oracle-arm64-jp --docker "host=ssh://root@100.107.174.88"
```
```bash
docker context create oracle-arm64-jp --docker "host=ssh://root@100.107.174.88"
```
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>
<p>
1. Draft a release note
2. Make sure the repo is cleared
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`
These Items need to be checked:
- [ ] Check all tags is fine on
<https://hub.docker.com/r/louislam/uptime-kuma/tags>
- [ ] 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>
<p>
1. Draft a release note, check `This is a pre-release`
2. Make sure the repo is cleared
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.
<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/">
<img src="https://weblate.kuma.pet/widgets/uptime-kuma/-/svg-badge.svg" alt="Translation status" />
</a>
@@ -45,6 +45,7 @@ cd uptime-kuma
curl -o compose.yaml https://raw.githubusercontent.com/louislam/uptime-kuma/master/compose.yaml
docker compose up -d
```
Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001 or http://your-ip:3001).
> [!WARNING]
@@ -55,6 +56,7 @@ Uptime Kuma is now running on all network interfaces (e.g. http://localhost:3001
```bash
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).
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 ...
```
### 💪🏻 Non-Docker
Requirements:
@@ -93,6 +93,7 @@ npm install pm2 -g && pm2 install pm2-logrotate
# Start Server
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).
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 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?

View File

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

View File

@@ -15,13 +15,13 @@ export default defineConfig({
port: 3000,
},
define: {
"FRONTEND_VERSION": JSON.stringify(process.env.npm_package_version),
FRONTEND_VERSION: JSON.stringify(process.env.npm_package_version),
"process.env": {},
},
plugins: [
vue(),
visualizer({
filename: "tmp/dist-stats.html"
filename: "tmp/dist-stats.html",
}),
viteCompression({
algorithm: "gzip",
@@ -40,21 +40,19 @@ export default defineConfig({
],
css: {
postcss: {
"parser": postCssScss,
"map": false,
"plugins": [ postcssRTLCSS ]
}
parser: postCssScss,
map: false,
plugins: [postcssRTLCSS],
},
},
build: {
commonjsOptions: {
include: [ /.js$/ ],
include: [/.js$/],
},
rollupOptions: {
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.string("protocol", 10).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.string("username", 255).nullable();
table.string("password", 255).nullable();
@@ -67,10 +67,7 @@ async function createTables() {
table.increments("id");
table.string("name", 150);
table.boolean("active").notNullable().defaultTo(true);
table.integer("user_id").unsigned()
.references("id").inTable("user")
.onDelete("SET NULL")
.onUpdate("CASCADE");
table.integer("user_id").unsigned().references("id").inTable("user").onDelete("SET NULL").onUpdate("CASCADE");
table.integer("interval").notNullable().defaultTo(20);
table.text("url");
table.string("type", 20);
@@ -83,7 +80,7 @@ async function createTables() {
table.boolean("ignore_tls").notNullable().defaultTo(false);
table.boolean("upside_down").notNullable().defaultTo(false);
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_server", 255);
table.string("dns_last_result", 255);
@@ -94,11 +91,9 @@ async function createTables() {
table.text("headers").defaultTo(null);
table.text("basic_auth_user").defaultTo(null);
table.text("basic_auth_pass").defaultTo(null);
table.integer("docker_host").unsigned()
.references("id").inTable("docker_host");
table.integer("docker_host").unsigned().references("id").inTable("docker_host");
table.string("docker_container", 255);
table.integer("proxy_id").unsigned()
.references("id").inTable("proxy");
table.integer("proxy_id").unsigned().references("id").inTable("proxy");
table.boolean("expiry_notification").defaultTo(true);
table.text("mqtt_topic");
table.string("mqtt_success_message", 255);
@@ -130,8 +125,12 @@ async function createTables() {
await knex.schema.createTable("heartbeat", (table) => {
table.increments("id");
table.boolean("important").notNullable().defaultTo(false);
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.smallint("status").notNullable();
@@ -143,9 +142,9 @@ async function createTables() {
table.integer("down_count").notNullable().defaultTo(0);
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", "important", "time" ], "monitor_important_time_index");
table.index(["monitor_id", "important", "time"], "monitor_important_time_index");
});
// incident
@@ -166,10 +165,7 @@ async function createTables() {
table.increments("id");
table.string("title", 150).notNullable();
table.text("description").notNullable();
table.integer("user_id").unsigned()
.references("id").inTable("user")
.onDelete("SET NULL")
.onUpdate("CASCADE");
table.integer("user_id").unsigned().references("id").inTable("user").onDelete("SET NULL").onUpdate("CASCADE");
table.boolean("active").notNullable().defaultTo(true);
table.string("strategy", 50).notNullable().defaultTo("single");
table.datetime("start_date");
@@ -181,7 +177,7 @@ async function createTables() {
table.integer("interval_day");
table.index("active");
table.index([ "strategy", "active" ], "manual_active");
table.index(["strategy", "active"], "manual_active");
table.index("user_id", "maintenance_user_id");
});
@@ -209,13 +205,21 @@ async function createTables() {
await knex.schema.createTable("maintenance_status_page", (table) => {
table.increments("id");
table.integer("status_page_id").unsigned().notNullable()
.references("id").inTable("status_page")
table
.integer("status_page_id")
.unsigned()
.notNullable()
.references("id")
.inTable("status_page")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("maintenance_id").unsigned().notNullable()
.references("id").inTable("maintenance")
table
.integer("maintenance_id")
.unsigned()
.notNullable()
.references("id")
.inTable("maintenance")
.onDelete("CASCADE")
.onUpdate("CASCADE");
});
@@ -223,8 +227,12 @@ async function createTables() {
// maintenance_timeslot
await knex.schema.createTable("maintenance_timeslot", (table) => {
table.increments("id");
table.integer("maintenance_id").unsigned().notNullable()
.references("id").inTable("maintenance")
table
.integer("maintenance_id")
.unsigned()
.notNullable()
.references("id")
.inTable("maintenance")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.datetime("start_date").notNullable();
@@ -232,35 +240,51 @@ async function createTables() {
table.boolean("generated_next").defaultTo(false);
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");
});
// monitor_group
await knex.schema.createTable("monitor_group", (table) => {
table.increments("id");
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("group_id").unsigned().notNullable()
.references("id").inTable("group")
table
.integer("group_id")
.unsigned()
.notNullable()
.references("id")
.inTable("group")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("weight").notNullable().defaultTo(1000);
table.boolean("send_url").notNullable().defaultTo(false);
table.index([ "monitor_id", "group_id" ], "fk");
table.index(["monitor_id", "group_id"], "fk");
});
// monitor_maintenance
await knex.schema.createTable("monitor_maintenance", (table) => {
table.increments("id");
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("maintenance_id").unsigned().notNullable()
.references("id").inTable("maintenance")
table
.integer("maintenance_id")
.unsigned()
.notNullable()
.references("id")
.inTable("maintenance")
.onDelete("CASCADE")
.onUpdate("CASCADE");
@@ -280,17 +304,25 @@ async function createTables() {
// monitor_notification
await knex.schema.createTable("monitor_notification", (table) => {
table.increments("id").unsigned(); // TODO: no auto increment????
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
table.increments("id").unsigned(); // TODO: no auto increment????
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("notification_id").unsigned().notNullable()
.references("id").inTable("notification")
table
.integer("notification_id")
.unsigned()
.notNullable()
.references("id")
.inTable("notification")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.index([ "monitor_id", "notification_id" ], "monitor_notification_index");
table.index(["monitor_id", "notification_id"], "monitor_notification_index");
});
// tag
@@ -304,12 +336,20 @@ async function createTables() {
// monitor_tag
await knex.schema.createTable("monitor_tag", (table) => {
table.increments("id");
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("tag_id").unsigned().notNullable()
.references("id").inTable("tag")
table
.integer("tag_id")
.unsigned()
.notNullable()
.references("id")
.inTable("tag")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.text("value");
@@ -318,8 +358,12 @@ async function createTables() {
// monitor_tls_info
await knex.schema.createTable("monitor_tls_info", (table) => {
table.increments("id");
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.text("info_json");
@@ -331,8 +375,8 @@ async function createTables() {
table.string("type", 50).notNullable();
table.integer("monitor_id").unsigned().notNullable();
table.integer("days").notNullable();
table.unique([ "type", "monitor_id", "days" ]);
table.index([ "type", "monitor_id", "days" ], "good_index");
table.unique(["type", "monitor_id", "days"]);
table.index(["type", "monitor_id", "days"], "good_index");
});
// setting
@@ -346,16 +390,19 @@ async function createTables() {
// status_page_cname
await knex.schema.createTable("status_page_cname", (table) => {
table.increments("id");
table.integer("status_page_id").unsigned()
.references("id").inTable("status_page")
table
.integer("status_page_id")
.unsigned()
.references("id")
.inTable("status_page")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.string("domain").notNullable().unique().collate("utf8_general_ci");
});
/*********************
* Converted Patch here
*********************/
* Converted Patch here
*********************/
// 2023-06-30-1348-http-body-encoding.js
// ALTER TABLE monitor ADD http_body_encoding VARCHAR(25);
@@ -396,8 +443,12 @@ async function createTables() {
table.increments("id").primary();
table.string("key", 255).notNullable();
table.string("name", 255).notNullable();
table.integer("user_id").unsigned().notNullable()
.references("id").inTable("user")
table
.integer("user_id")
.unsigned()
.notNullable()
.references("id")
.inTable("user")
.onDelete("CASCADE")
.onUpdate("CASCADE");
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 duration INTEGER;
*/
await knex.schema
.dropTableIfExists("maintenance_timeslot")
.table("maintenance", function (table) {
table.text("cron");
table.string("timezone", 255);
table.integer("duration");
});
await knex.schema.dropTableIfExists("maintenance_timeslot").table("maintenance", function (table) {
table.text("cron");
table.string("timezone", 255);
table.integer("duration");
});
// 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;
*/
await knex.schema.table("monitor", function (table) {
table.integer("parent").unsigned()
.references("id").inTable("monitor")
.onDelete("SET NULL")
.onUpdate("CASCADE");
table.integer("parent").unsigned().references("id").inTable("monitor").onDelete("SET NULL").onUpdate("CASCADE");
});
/*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,29 @@
exports.up = function (knex) {
return knex.schema
.alterTable("stat_daily", function (table) {
table.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");
table
.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) {
table.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");
table
.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) {

View File

@@ -1,26 +1,26 @@
exports.up = function (knex) {
return knex.schema
.createTable("stat_hourly", function (table) {
table.increments("id");
table.comment("This table contains the hourly aggregate statistics for each monitor");
table.integer("monitor_id").unsigned().notNullable()
.references("id").inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("timestamp")
.notNullable()
.comment("Unix timestamp rounded down to the nearest hour");
table.float("ping").notNullable().comment("Average ping in milliseconds");
table.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");
table.smallint("up").notNullable();
table.smallint("down").notNullable();
return knex.schema.createTable("stat_hourly", function (table) {
table.increments("id");
table.comment("This table contains the hourly aggregate statistics for each monitor");
table
.integer("monitor_id")
.unsigned()
.notNullable()
.references("id")
.inTable("monitor")
.onDelete("CASCADE")
.onUpdate("CASCADE");
table.integer("timestamp").notNullable().comment("Unix timestamp rounded down to the nearest hour");
table.float("ping").notNullable().comment("Average ping in milliseconds");
table.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");
table.smallint("up").notNullable();
table.smallint("down").notNullable();
table.unique([ "monitor_id", "timestamp" ]);
});
table.unique(["monitor_id", "timestamp"]);
});
};
exports.down = function (knex) {
return knex.schema
.dropTable("stat_hourly");
return knex.schema.dropTable("stat_hourly");
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,12 @@ exports.up = function (knex) {
.alterTable("status_page", function (table) {
table.renameColumn("google_analytics_tag_id", "analytics_id");
table.string("analytics_script_url");
table.enu("analytics_type", [ "google", "umami", "plausible", "matomo" ]).defaultTo(null);
}).then(() => {
table.enu("analytics_type", ["google", "umami", "plausible", "matomo"]).defaultTo(null);
})
.then(() => {
// After a succesful migration, add google as default for previous pages
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) {
// Add new columns to table monitor
return knex.schema
.alterTable("monitor", function (table) {
table.integer("ping_count").defaultTo(1).notNullable();
table.boolean("ping_numeric").defaultTo(true).notNullable();
table.integer("ping_per_request_timeout").defaultTo(2).notNullable();
});
return knex.schema.alterTable("monitor", function (table) {
table.integer("ping_count").defaultTo(1).notNullable();
table.boolean("ping_numeric").defaultTo(true).notNullable();
table.integer("ping_per_request_timeout").defaultTo(2).notNullable();
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.dropColumn("ping_count");
table.dropColumn("ping_numeric");
table.dropColumn("ping_per_request_timeout");
});
return knex.schema.alterTable("monitor", function (table) {
table.dropColumn("ping_count");
table.dropColumn("ping_numeric");
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
exports.up = function (knex) {
return knex.schema
.alterTable("proxy", function (table) {
table.integer("port").alter();
});
return knex.schema.alterTable("proxy", function (table) {
table.integer("port").alter();
});
};
exports.down = function (knex) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,11 +9,11 @@ exports.up = async function (knex) {
// Create partial indexes with predicate
await knex.schema.alterTable("heartbeat", function (table) {
table.index([ "monitor_id", "time" ], "monitor_important_time_index", {
predicate: knex.whereRaw("important = 1")
table.index(["monitor_id", "time"], "monitor_important_time_index", {
predicate: knex.whereRaw("important = 1"),
});
table.index([ "important" ], "heartbeat_important_index", {
predicate: knex.whereRaw("important = 1")
table.index(["important"], "heartbeat_important_index", {
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.schema.alterTable("heartbeat", function (table) {
table.index([ "monitor_id", "important", "time" ], "monitor_important_time_index");
table.index([ "important" ]);
table.index(["monitor_id", "important", "time"], "monitor_important_time_index");
table.index(["important"]);
});
}
// 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
exports.up = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.text("dns_last_result").alter();
});
return knex.schema.alterTable("monitor", function (table) {
table.text("dns_last_result").alter();
});
};
exports.down = function (knex) {
return knex.schema
.alterTable("monitor", function (table) {
table.string("dns_last_result", 255).alter();
});
return knex.schema.alterTable("monitor", function (table) {
table.string("dns_last_result", 255).alter();
});
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,27 +16,26 @@ if (!version || !version.includes("-beta.")) {
const exists = tagExists(version);
if (! exists) {
if (!exists) {
// Process package.json
pkg.version = version;
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n");
// Also update package-lock.json
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) {
console.error(resultVersion.error);
console.error("error npm version!");
process.exit(1);
}
const resultInstall = childProcess.spawnSync(npm, [ "install" ], { shell: true });
const resultInstall = childProcess.spawnSync(npm, ["install"], { shell: true });
if (resultInstall.error) {
console.error(resultInstall.error);
console.error("error update package-lock!");
process.exit(1);
}
commit(version);
} else {
console.log("version tag exists, please delete the tag or use another tag");
process.exit(1);
@@ -51,7 +50,7 @@ if (! exists) {
function commit(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();
console.log(stdout);
@@ -59,7 +58,7 @@ function commit(version) {
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());
}
@@ -70,11 +69,11 @@ function commit(version) {
* @throws Version is not valid
*/
function tagExists(version) {
if (! version) {
if (!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;
}

View File

@@ -14,7 +14,9 @@ if (platform === "linux/arm/v7") {
console.log("Already built in the host, skip.");
process.exit(0);
} 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 {
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");
console.log(output);

View File

@@ -18,7 +18,7 @@ const github = require("@actions/github");
await client.issues.listLabelsOnIssue({
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number
issue_number: issue.number,
})
).data.map(({ name }) => name);
@@ -29,7 +29,7 @@ const github = require("@actions/github");
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
labels: [ "invalid-format" ]
labels: ["invalid-format"],
});
// Add the issue closing comment
@@ -37,7 +37,7 @@ const github = require("@actions/github");
owner: issue.owner,
repo: issue.repo,
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
@@ -45,7 +45,7 @@ const github = require("@actions/github");
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
state: "closed"
state: "closed",
});
} else {
console.log("Pass!");
@@ -53,5 +53,4 @@ const github = require("@actions/github");
} catch (e) {
console.log(e);
}
})();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ async function main() {
const branch = process.argv[2];
// 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);
// 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 rl = readline.createInterface({
input: process.stdin,
output: process.stdout
output: process.stdout,
});
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.
if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user");
if (! user) {
if (!user) {
throw new Error("user not found, have you installed?");
}
@@ -31,7 +31,6 @@ const main = async () => {
await TwoFA.disable2FA(user.id);
console.log("2FA has been removed successfully.");
}
}
} catch (e) {
console.error("Error: " + e.message);

View File

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

View File

@@ -12,7 +12,7 @@ const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
output: process.stdout,
});
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.
if (!process.env.TEST_BACKEND) {
const user = await R.findOne("user");
if (! user) {
if (!user) {
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)
if ("new-password" in args) {
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"] + "";
if (passwordStrength(password).value === "Too weak") {
throw new Error("Password is too weak, please use a stronger password.");
@@ -71,7 +74,6 @@ const main = async () => {
}
}
console.log("Password reset successfully.");
}
} catch (e) {
console.error("Error: " + e.message);
@@ -112,19 +114,23 @@ function disconnectAllSocketClients(username, password) {
timeout: 5000,
});
socket.on("connect", () => {
socket.emit("login", {
username,
password,
}, (res) => {
if (res.ok) {
console.log("Logged in.");
socket.emit("disconnectOtherSocketClients");
} else {
console.warn("Login failed.");
console.warn("Please restart the server to disconnect all sessions.");
socket.emit(
"login",
{
username,
password,
},
(res) => {
if (res.ok) {
console.log("Logged in.");
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 () {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,15 +9,14 @@ const newVersion = process.env.RELEASE_VERSION;
console.log("New Version: " + newVersion);
if (! newVersion) {
if (!newVersion) {
console.error("invalid version");
process.exit(1);
}
const exists = tagExists(newVersion);
if (! exists) {
if (!exists) {
// Process package.json
pkg.version = newVersion;
@@ -27,20 +26,19 @@ if (! exists) {
// Also update package-lock.json
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) {
console.error(resultVersion.error);
console.error("error npm version!");
process.exit(1);
}
const resultInstall = childProcess.spawnSync(npm, [ "install" ], { shell: true });
const resultInstall = childProcess.spawnSync(npm, ["install"], { shell: true });
if (resultInstall.error) {
console.error(resultInstall.error);
console.error("error update package-lock!");
process.exit(1);
}
commit(newVersion);
} else {
console.log("version exists");
}
@@ -54,7 +52,7 @@ if (! exists) {
function commit(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();
console.log(stdout);
@@ -70,11 +68,11 @@ function commit(version) {
* @throws Version is not valid
*/
function tagExists(version) {
if (! version) {
if (!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;
}

View File

@@ -21,23 +21,23 @@ function updateWiki(newVersion) {
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();
// Replace the version: https://regex101.com/r/hmj2Bc/1
content = content.replace(/(git checkout )([^\s]+)/, `$1${newVersion}`);
fs.writeFileSync(howToUpdateFilename, content);
childProcess.spawnSync("git", [ "add", "-A" ], {
childProcess.spawnSync("git", ["add", "-A"], {
cwd: wikiDir,
});
childProcess.spawnSync("git", [ "commit", "-m", `Update to ${newVersion}` ], {
childProcess.spawnSync("git", ["commit", "-m", `Update to ${newVersion}`], {
cwd: wikiDir,
});
console.log("Pushing to Github");
childProcess.spawnSync("git", [ "push" ], {
childProcess.spawnSync("git", ["push"], {
cwd: wikiDir,
});

View File

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

View File

@@ -1,13 +1,13 @@
{
"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-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-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-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-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"
}
"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-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-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-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-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"
}
}

40062
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,88 +1,88 @@
{
"name": "Uptime Kuma",
"short_name": "Uptime Kuma",
"description": "An easy-to-use self-hosted monitoring tool.",
"theme_color": "#5cdd8b",
"start_url": "/",
"background_color": "#fff",
"display": "standalone",
"icons": [
"name": "Uptime Kuma",
"short_name": "Uptime Kuma",
"description": "An easy-to-use self-hosted monitoring tool.",
"theme_color": "#5cdd8b",
"start_url": "/",
"background_color": "#fff",
"display": "standalone",
"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",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
"src": "icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"shortcuts": [
]
},
{
"name": "Add Monitor",
"short_name": "Add Monitor",
"description": "Add a new monitor",
"url": "/add",
"icons": [
{
"name": "Dashboard",
"short_name": "Dashboard",
"description": "View monitoring dashboard",
"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"
}
]
"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");
class TwoFA {
/**
* Disable 2FA for specified user
* @param {number} userID ID of user to disable
* @returns {Promise<void>}
*/
static async disable2FA(userID) {
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [
userID,
]);
return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [userID]);
}
}
module.exports = TwoFA;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
const isFreeBSD = /^freebsd/.test(process.platform);
// 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.
// 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;
const hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv;
const port = [ args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001 ]
.map(portValue => parseInt(portValue))
.find(portValue => !isNaN(portValue));
const port = [args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001]
.map((portValue) => parseInt(portValue))
.find((portValue) => !isNaN(portValue));
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 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;

View File

@@ -18,7 +18,6 @@ const SqlString = require("sqlstring");
* Database & App Data Folder
*/
class Database {
/**
* Bootstrap database for SQLite
* @type {string}
@@ -89,7 +88,7 @@ class Database {
"patch-added-mqtt-monitor.sql": true,
"patch-add-clickable-status-page-link.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-add-radius-monitor.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.sqlitePath = path.join(Database.dataDir, "kuma.db");
if (! fs.existsSync(Database.dataDir)) {
if (!fs.existsSync(Database.dataDir)) {
fs.mkdirSync(Database.dataDir, { recursive: true });
}
Database.uploadDir = path.join(Database.dataDir, "upload/");
if (! fs.existsSync(Database.uploadDir)) {
if (!fs.existsSync(Database.uploadDir)) {
fs.mkdirSync(Database.uploadDir, { recursive: true });
}
// Create screenshot dir
Database.screenshotDir = path.join(Database.dataDir, "screenshots/");
if (! fs.existsSync(Database.screenshotDir)) {
if (!fs.existsSync(Database.screenshotDir)) {
fs.mkdirSync(Database.screenshotDir, { recursive: true });
}
Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/");
if (! fs.existsSync(Database.dockerTLSDir)) {
if (!fs.existsSync(Database.dockerTLSDir)) {
fs.mkdirSync(Database.dockerTLSDir, { recursive: true });
}
@@ -228,13 +227,22 @@ class Database {
if (!process.env.UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS) {
parsedMaxPoolConnections = 10;
} 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;
} 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;
} 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;
}
@@ -247,8 +255,7 @@ class Database {
log.info("db", `Database Type: ${dbConfig.type}`);
if (dbConfig.type === "sqlite") {
if (! fs.existsSync(Database.sqlitePath)) {
if (!fs.existsSync(Database.sqlitePath)) {
log.info("server", "Copying Database");
fs.copyFileSync(Database.templatePath, Database.sqlitePath);
}
@@ -269,7 +276,7 @@ class Database {
idleTimeoutMillis: 120 * 1000,
propagateCreateError: false,
acquireTimeoutMillis: acquireConnectionTimeout,
}
},
};
} else if (dbConfig.type === "mariadb") {
const connection = await mysql.createConnection({
@@ -387,7 +394,7 @@ class Database {
log.debug("db", "SQLite config:");
log.debug("db", await R.getAll("PRAGMA journal_mode"));
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);
} catch (e) {
// Allow missing patch files for downgrade or testing pr.
if (e.message.includes("the following files are missing:")) {
@@ -456,9 +462,7 @@ class Database {
* TODO
* @returns {Promise<void>}
*/
static async rollbackLatestPatch() {
}
static async rollbackLatestPatch() {}
/**
* Patch the database for SQLite
@@ -468,7 +472,7 @@ class Database {
static async patchSqlite() {
let version = parseInt(await setting("database_version"));
if (! version) {
if (!version) {
version = 0;
}
@@ -498,7 +502,10 @@ class Database {
log.error("db", ex);
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);
}
@@ -519,7 +526,7 @@ class Database {
log.debug("db", "Database Patch 2.0 Process");
let databasePatchedFiles = await setting("databasePatchedFiles");
if (! databasePatchedFiles) {
if (!databasePatchedFiles) {
databasePatchedFiles = {};
}
@@ -534,13 +541,15 @@ class Database {
if (this.patched) {
log.info("db", "Database Patched Successfully");
}
} catch (ex) {
await Database.close();
log.error("db", ex);
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);
}
@@ -554,7 +563,6 @@ class Database {
* @returns {Promise<void>}
*/
static async migrateNewStatusPage() {
// Fix 1.13.0 empty slug bug
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.icon = await setting("icon");
statusPage.theme = await setting("statusPageTheme");
statusPage.published = !!await setting("statusPagePublished");
statusPage.search_engine_index = !!await setting("searchEngineIndex");
statusPage.show_tags = !!await setting("statusPageTags");
statusPage.published = !!(await setting("statusPagePublished"));
statusPage.search_engine_index = !!(await setting("searchEngineIndex"));
statusPage.show_tags = !!(await setting("statusPageTags"));
statusPage.password = null;
if (!statusPage.title) {
@@ -595,13 +603,9 @@ class Database {
let id = await R.store(statusPage);
await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [
id
]);
await R.exec("UPDATE incident SET status_page_id = ? WHERE status_page_id IS NULL", [id]);
await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [
id
]);
await R.exec("UPDATE [group] SET status_page_id = ? WHERE status_page_id IS NULL", [id]);
await R.exec("DELETE FROM setting WHERE type = 'statusPage'");
@@ -614,7 +618,6 @@ class Database {
console.log("Migrating Status Page - Done");
}
}
/**
@@ -628,13 +631,13 @@ class Database {
static async patch2Recursion(sqlFilename, databasePatchedFiles) {
let value = this.patchList[sqlFilename];
if (! value) {
if (!value) {
log.info("db", sqlFilename + " skip");
return;
}
// Check if patched
if (! databasePatchedFiles[sqlFilename]) {
if (!databasePatchedFiles[sqlFilename]) {
log.info("db", sqlFilename + " is not patched");
if (value.parents) {
@@ -649,7 +652,6 @@ class Database {
await this.importSQLFile("./db/old_migrations/" + sqlFilename);
databasePatchedFiles[sqlFilename] = true;
log.info("db", sqlFilename + " was patched successfully");
} else {
log.debug("db", sqlFilename + " is already patched, skip");
}
@@ -669,14 +671,15 @@ class Database {
// Remove all comments (--)
let lines = text.split("\n");
lines = lines.filter((line) => {
return ! line.startsWith("--");
return !line.startsWith("--");
});
// Split statements by semicolon
// Filter out empty line
text = lines.join("\n");
let statements = text.split(";")
let statements = text
.split(";")
.map((statement) => {
return statement.trim();
})
@@ -773,7 +776,10 @@ class Database {
// Add a setting for 2.0.0-dev users to skip this migration
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");
}
@@ -813,11 +819,14 @@ class Database {
`);
// 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 count = countResult.count;
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();
return;
}
@@ -826,31 +835,35 @@ class Database {
await Settings.set("migrateAggregateTableState", "migrating");
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
let dates = await R.getAll(`
let dates = await R.getAll(
`
SELECT DISTINCT DATE(time) AS date
FROM heartbeat
WHERE monitor_id = ?
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
let calculator = new UptimeCalculator();
calculator.monitorID = monitor.monitor_id;
calculator.setMigrationMode(true);
// Get all the heartbeats for this monitor and date
let heartbeats = await R.getAll(`
let heartbeats = await R.getAll(
`
SELECT status, ping, time
FROM heartbeat
WHERE monitor_id = ?
AND DATE(time) = ?
ORDER BY time ASC
`, [ monitor.monitor_id, date.date ]);
`,
[monitor.monitor_id, date.date]
);
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)}%`;
@@ -863,7 +876,7 @@ class Database {
}
// 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
if (progressPercent > 100) {
@@ -900,7 +913,8 @@ class Database {
if (detailedLog) {
log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id);
}
await R.exec(`
await R.exec(
`
DELETE FROM heartbeat
WHERE monitor_id = ?
AND important = 0
@@ -914,15 +928,11 @@ class Database {
LIMIT ?
) AS limited_ids
)
`, [
monitor.id,
-24,
monitor.id,
100,
]);
`,
[monitor.id, -24, monitor.id, 100]
);
}
}
}
module.exports = Database;

View File

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

View File

@@ -7,7 +7,6 @@ const mysql = require("mysql2");
* It is only used inside the docker container
*/
class EmbeddedMariaDB {
static instance = null;
exec = "mariadbd";
@@ -59,7 +58,9 @@ class EmbeddedMariaDB {
// Check if the current user is "node" or "root"
this.username = require("os").userInfo().username;
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();
@@ -211,7 +212,6 @@ class EmbeddedMariaDB {
log.info("mariadb", "Embedded MariaDB is ready for connections");
this.started = true;
}
}
module.exports = {

View File

@@ -6,7 +6,6 @@ let fs = require("fs");
const { log } = require("../src/util");
let ImageDataURI = (() => {
/**
* Decode the data:image/ URI
* @param {string} dataURI data:image/ URI to decode
@@ -17,7 +16,7 @@ let ImageDataURI = (() => {
*/
function decode(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;
}
@@ -25,7 +24,7 @@ let ImageDataURI = (() => {
return {
imageType: regExMatches[1],
dataBase64: regExMatches[2],
dataBuffer: new Buffer(regExMatches[2], "base64")
dataBuffer: new Buffer(regExMatches[2], "base64"),
};
}
@@ -42,8 +41,8 @@ let ImageDataURI = (() => {
return null;
}
mediaType = (/\//.test(mediaType)) ? mediaType : "image/" + mediaType;
let dataBase64 = (Buffer.isBuffer(data)) ? data.toString("base64") : new Buffer(data).toString("base64");
mediaType = /\//.test(mediaType) ? mediaType : "image/" + mediaType;
let dataBase64 = Buffer.isBuffer(data) ? data.toString("base64") : new Buffer(data).toString("base64");
let dataImgBase64 = "data:" + mediaType + ";base64," + dataBase64;
return dataImgBase64;
@@ -60,7 +59,7 @@ let ImageDataURI = (() => {
return new Promise((resolve, reject) => {
let imageDecoded = decode(dataURI);
fs.writeFile(filePath, imageDecoded.dataBuffer, err => {
fs.writeFile(filePath, imageDecoded.dataBuffer, (err) => {
if (err) {
return reject("ImageDataURI :: Error :: " + JSON.stringify(err, null, 4));
}

View File

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

View File

@@ -31,23 +31,22 @@ const clearOldData = async () => {
}
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 {
log.debug("clearOldData", `Clearing Data older than ${parsedPeriod} days...`);
const sqlHourOffset = Database.sqlHourOffset();
try {
// Heartbeat
await R.exec("DELETE FROM heartbeat WHERE time < " + sqlHourOffset, [
parsedPeriod * -24,
]);
await R.exec("DELETE FROM heartbeat WHERE time < " + sqlHourOffset, [parsedPeriod * -24]);
let timestamp = dayjs().subtract(parsedPeriod, "day").utc().startOf("day").unix();
// stat_daily
await R.exec("DELETE FROM stat_daily WHERE timestamp < ? ", [
timestamp,
]);
await R.exec("DELETE FROM stat_daily WHERE timestamp < ? ", [timestamp]);
if (Database.dbConfig.type === "sqlite") {
await R.exec("PRAGMA optimize;");

View File

@@ -7,10 +7,12 @@ const { Notification } = require("../notification");
const { default: NodeFetchCache, MemoryCache } = require("node-fetch-cache");
const TranslatableError = require("../translatable-error");
const cachedFetch = process.env.NODE_ENV ? NodeFetchCache.create({
// cache for 8h
cache: new MemoryCache({ ttl: 1000 * 60 * 60 * 8 })
}) : fetch;
const cachedFetch = process.env.NODE_ENV
? NodeFetchCache.create({
// cache for 8h
cache: new MemoryCache({ ttl: 1000 * 60 * 60 * 8 }),
})
: fetch;
/**
* Find the RDAP server for a given TLD
@@ -28,7 +30,7 @@ async function getRdapServer(tld) {
}
for (const service of rdapList["services"]) {
const [ tlds, urls ] = service;
const [tlds, urls] = service;
if (tlds.includes(tld)) {
return urls[0];
}
@@ -108,7 +110,7 @@ class DomainExpiry extends BeanModel {
* @returns {Promise<DomainExpiry>} Domain bean
*/
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) {
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 });
}
if (tld.isIp) {
@@ -176,9 +178,14 @@ class DomainExpiry extends BeanModel {
// Only warn when the monitor actually has domain expiry notifications enabled.
// The edit monitor page calls this method frequently while the user is typing.
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 {
@@ -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.
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;
}
@@ -269,8 +279,8 @@ class DomainExpiry extends BeanModel {
let notifyDays = await setting("domainExpiryNotifyDays");
if (notifyDays == null || !Array.isArray(notifyDays)) {
// Reset Default
await setSetting("domainExpiryNotifyDays", [ 7, 14, 21 ], "general");
notifyDays = [ 7, 14, 21 ];
await setSetting("domainExpiryNotifyDays", [7, 14, 21], "general");
notifyDays = [7, 14, 21];
}
if (Array.isArray(notifyDays)) {
// 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");
class Group extends BeanModel {
/**
* Return an object that ready to parse to JSON for public Only show
* necessary data to public
@@ -32,14 +31,18 @@ class Group extends BeanModel {
* @returns {Promise<Bean[]>} List of monitors
*/
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
WHERE monitor.id = monitor_group.monitor_id
AND group_id = ?
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
*/
class Heartbeat extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
@@ -18,7 +17,7 @@ class Heartbeat extends BeanModel {
return {
status: this.status,
time: this.time,
msg: "", // Hide for public
msg: "", // Hide for public
ping: this.ping,
};
}
@@ -39,7 +38,6 @@ class Heartbeat extends BeanModel {
retries: this._retries,
};
}
}
module.exports = Heartbeat;

View File

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

View File

@@ -7,14 +7,12 @@ const { UptimeKumaServer } = require("../uptime-kuma-server");
const apicache = require("../modules/apicache");
class Maintenance extends BeanModel {
/**
* Return an object that ready to parse to JSON for public
* Only show necessary data to public
* @returns {Promise<object>} Object ready to parse
*/
async toPublicJSON() {
let dateRange = [];
if (this.start_date) {
dateRange.push(this.start_date);
@@ -41,14 +39,14 @@ class Maintenance extends BeanModel {
active: !!this.active,
dateRange: dateRange,
timeRange: timeRange,
weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [],
daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [],
weekdays: this.weekdays ? JSON.parse(this.weekdays) : [],
daysOfMonth: this.days_of_month ? JSON.parse(this.days_of_month) : [],
timeslotList: [],
cron: this.cron,
duration: this.duration,
durationMinutes: parseInt(this.duration / 60),
timezone: await this.getTimezone(), // Only valid timezone
timezoneOption: this.timezone, // Mainly for dropdown menu, because there is a option "SAME_AS_SERVER"
timezone: await this.getTimezone(), // Only valid timezone
timezoneOption: this.timezone, // Mainly for dropdown menu, because there is a option "SAME_AS_SERVER"
timezoneOffset: await this.getTimezoneOffset(),
status: await this.getStatus(),
};
@@ -202,7 +200,7 @@ class Maintenance extends BeanModel {
* @returns {void}
*/
static validateCron(cron) {
let job = new Cron(cron, () => { });
let job = new Cron(cron, () => {});
job.stop();
}
@@ -270,7 +268,7 @@ class Maintenance extends BeanModel {
if (this.strategy === "recurring-interval") {
// For recurring-interval, Croner needs to have interval and startAt
const startDate = dayjs(this.startDate);
const [ hour, minute ] = this.startTime.split(":");
const [hour, minute] = this.startTime.split(":");
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()
@@ -279,31 +277,44 @@ class Maintenance extends BeanModel {
startAt = startDateTime.toISOString();
} catch (_) {}
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
startAt,
}, () => {
if (!this.lastStartDate || this.interval_day === 1) {
this.beanMeta.job = new Cron(
this.cron,
{
timezone: await this.getTimezone(),
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();
}
// 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 {
this.beanMeta.job = new Cron(this.cron, {
timezone: await this.getTimezone(),
}, startEvent);
this.beanMeta.job = new Cron(
this.cron,
{
timezone: await this.getTimezone(),
},
startEvent
);
}
// 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");
startEvent(duration);
}
} catch (e) {
log.error("maintenance", "Error in maintenance id: " + this.id);
log.error("maintenance", "Cron: " + this.cron);
@@ -324,7 +334,6 @@ class Maintenance extends BeanModel {
throw e;
}
}
} else {
log.error("maintenance", "Maintenance id: " + this.id + " has no cron");
}
@@ -486,12 +495,11 @@ class Maintenance extends BeanModel {
}
// Remove duplicate
dayList = [ ...new Set(dayList) ];
dayList = [...new Set(dayList)];
this.cron = minute + " " + hour + " " + dayList.join(",") + " * *";
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 { 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 {
/**
* Like this: { "test-uptime.kuma.pet": "default" }
* @type {{}}
*/
static domainMappingList = { };
static domainMappingList = {};
/**
* Handle responses to RSS pages
@@ -27,9 +34,7 @@ class StatusPage extends BeanModel {
* @returns {Promise<void>}
*/
static async handleStatusPageRSSResponse(response, slug, request) {
let statusPage = await R.findOne("status_page", " slug = ? ", [
slug
]);
let statusPage = await R.findOne("status_page", " slug = ? ", [slug]);
if (statusPage) {
const feedUrl = await StatusPage.buildRSSUrl(slug, request);
@@ -54,9 +59,7 @@ class StatusPage extends BeanModel {
slug = "default";
}
let statusPage = await R.findOne("status_page", " slug = ? ", [
slug
]);
let statusPage = await R.findOne("status_page", " slug = ? ", [slug]);
if (statusPage) {
response.send(await StatusPage.renderHTML(indexHTML, statusPage));
@@ -90,7 +93,7 @@ class StatusPage extends BeanModel {
updated: new Date(), // optional, default = today
});
heartbeats.forEach(heartbeat => {
heartbeats.forEach((heartbeat) => {
feed.addItem({
title: `${heartbeat.name} is down`,
description: `${heartbeat.name} has been down since ${heartbeat.time} UTC`,
@@ -153,9 +156,7 @@ class StatusPage extends BeanModel {
$("meta[name=description]").attr("content", description155);
if (statusPage.icon) {
$("link[rel=icon]")
.attr("href", statusPage.icon)
.removeAttr("type");
$("link[rel=icon]").attr("href", statusPage.icon).removeAttr("type");
$("link[rel=apple-touch-icon]").remove();
}
@@ -168,19 +169,19 @@ class StatusPage extends BeanModel {
}
// 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);
let ogDescription = $("<meta property=\"og:description\" content=\"\" />").attr("content", description155);
let ogDescription = $('<meta property="og:description" content="" />').attr("content", description155);
head.append(ogDescription);
let ogType = $("<meta property=\"og:type\" content=\"website\" />");
let ogType = $('<meta property="og:type" content="website" />');
head.append(ogType);
// Preload data
// Add jsesc, fix https://github.com/louislam/uptime-kuma/issues/2186
const escapedJSONObject = jsesc(await StatusPage.getStatusPageData(statusPage), {
"isScriptContext": true
isScriptContext: true,
});
const script = $(`
@@ -219,7 +220,7 @@ class StatusPage extends BeanModel {
}
}
if (! hasUp) {
if (!hasUp) {
status = STATUS_PAGE_ALL_DOWN;
}
@@ -267,21 +268,19 @@ class StatusPage extends BeanModel {
// Public Group List
const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
statusPage.id
]);
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [statusPage.id]);
let heartbeats = [];
for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry);
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) {
heartbeats.push({
...monitor,
status: heartbeat.status,
time: heartbeat.time
time: heartbeat.time,
});
}
}
@@ -292,11 +291,11 @@ class StatusPage extends BeanModel {
let statusDescription = StatusPage.getStatusDescription(status);
// keep only DOWN heartbeats in the RSS feed
heartbeats = heartbeats.filter(heartbeat => heartbeat.status === DOWN);
heartbeats = heartbeats.filter((heartbeat) => heartbeat.status === DOWN);
return {
heartbeats,
statusDescription
statusDescription,
};
}
@@ -309,9 +308,7 @@ class StatusPage extends BeanModel {
const config = await statusPage.toPublicJSON();
// Incident
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
statusPage.id,
]);
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [statusPage.id]);
if (incident) {
incident = incident.toPublicJSON();
@@ -323,9 +320,7 @@ class StatusPage extends BeanModel {
const publicGroupList = [];
const showTags = !!statusPage.show_tags;
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [
statusPage.id
]);
const list = await R.find("group", " public = 1 AND status_page_id = ? ORDER BY weight ", [statusPage.id]);
for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry);
@@ -379,16 +374,13 @@ class StatusPage extends BeanModel {
* @returns {Promise<void>}
*/
async updateDomainNameList(domainNameList) {
if (!Array.isArray(domainNameList)) {
throw new Error("Invalid array");
}
let trx = await R.begin();
await trx.exec("DELETE FROM status_page_cname WHERE status_page_id = ?", [
this.id,
]);
await trx.exec("DELETE FROM status_page_cname WHERE status_page_id = ?", [this.id]);
try {
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
await trx.exec("DELETE FROM status_page_cname WHERE domain = ?", [
domain,
]);
await trx.exec("DELETE FROM status_page_cname WHERE domain = ?", [domain]);
let mapping = trx.dispense("status_page_cname");
mapping.status_page_id = this.id;
@@ -494,9 +484,7 @@ class StatusPage extends BeanModel {
* @returns {Promise<number>} ID of status page
*/
static async slugToID(slug) {
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [
slug
]);
return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [slug]);
}
/**
@@ -520,21 +508,23 @@ class StatusPage extends BeanModel {
try {
const publicMaintenanceList = [];
let maintenanceIDList = await R.getCol(`
let maintenanceIDList = await R.getCol(
`
SELECT DISTINCT maintenance_id
FROM maintenance_status_page
WHERE status_page_id = ?
`, [ statusPageId ]);
`,
[statusPageId]
);
for (const maintenanceID of maintenanceIDList) {
let maintenance = UptimeKumaServer.getInstance().getMaintenance(maintenanceID);
if (maintenance && await maintenance.isUnderMaintenance()) {
if (maintenance && (await maintenance.isUnderMaintenance())) {
publicMaintenanceList.push(await maintenance.toPublicJSON());
}
}
return publicMaintenanceList;
} catch (error) {
return [];
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@ function MemoryCache() {
}
/**
*
*
* @param {string} key Key to store cache as
* @param {any} value Value to store
* @param {number} time Time to store for
@@ -22,7 +22,7 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
timeout: setTimeout(function () {
instance.delete(key);
return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key);
}, time)
}, time),
};
this.cache[key] = entry;
@@ -52,7 +52,7 @@ MemoryCache.prototype.delete = function (key) {
/**
* Get value of key
* @param {string} key
* @param {string} key
* @returns {Object}
*/
MemoryCache.prototype.get = function (key) {
@@ -63,7 +63,7 @@ MemoryCache.prototype.get = function (key) {
/**
* Get value of cache entry
* @param {string} key
* @param {string} key
* @returns {any}
*/
MemoryCache.prototype.getValue = function (key) {

View File

@@ -1,4 +1,4 @@
'use strict';
"use strict";
// Original file https://raw.githubusercontent.com/elasticio/node-ntlm-client/master/lib/flags.js
module.exports.NTLMFLAG_NEGOTIATE_UNICODE = 1 << 0;
/* Indicates that Unicode strings are supported for use in security buffer
@@ -74,4 +74,4 @@ module.exports.NTLMFLAG_NEGOTIATE_KEY_EXCHANGE = 1 << 30;
/* Indicates that the client will provide an encrypted master key in
the "Session Key" field of the Type 3 message. */
module.exports.NTLMFLAG_NEGOTIATE_56 = 1 << 31;
//# sourceMappingURL=flags.js.map
//# sourceMappingURL=flags.js.map

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
var crypto = require('crypto');
var crypto = require("crypto");
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);
calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf);
calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8);
@@ -10,40 +11,40 @@ function createLMResponse(challenge, lmhash) {
return buf;
}
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) {
buf.fill(0);
return buf;
}
pwBuffer.fill(0);
pwBuffer.write(password.toUpperCase(), 0, 'ascii');
return Buffer.concat([
calculateDES(pwBuffer.slice(0, 7), magicKey),
calculateDES(pwBuffer.slice(7), magicKey)
]);
pwBuffer.write(password.toUpperCase(), 0, "ascii");
return Buffer.concat([calculateDES(pwBuffer.slice(0, 7), magicKey), calculateDES(pwBuffer.slice(7), magicKey)]);
}
function calculateDES(key, message) {
var desKey = new Buffer.alloc(8);
desKey[0] = key[0] & 0xFE;
desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1);
desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2);
desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3);
desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4);
desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5);
desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6);
desKey[7] = (key[6] << 1) & 0xFF;
desKey[0] = key[0] & 0xfe;
desKey[1] = ((key[0] << 7) & 0xff) | (key[1] >> 1);
desKey[2] = ((key[1] << 6) & 0xff) | (key[2] >> 2);
desKey[3] = ((key[2] << 5) & 0xff) | (key[3] >> 3);
desKey[4] = ((key[3] << 4) & 0xff) | (key[4] >> 4);
desKey[5] = ((key[4] << 3) & 0xff) | (key[5] >> 5);
desKey[6] = ((key[5] << 2) & 0xff) | (key[6] >> 6);
desKey[7] = (key[6] << 1) & 0xff;
for (var i = 0; i < 8; i++) {
var parity = 0;
for (var j = 1; j < 8; j++) {
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);
}
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);
calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf);
calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8);
@@ -51,21 +52,23 @@ function createNTLMResponse(challenge, ntlmhash) {
return buf;
}
function createNTLMHash(password) {
var md4sum = crypto.createHash('md4');
md4sum.update(new Buffer.from(password, 'ucs2'));
var md4sum = crypto.createHash("md4");
md4sum.update(new Buffer.from(password, "ucs2"));
return md4sum.digest();
}
function createNTLMv2Hash(ntlmhash, username, authTargetName) {
var hmac = crypto.createHmac('md5', ntlmhash);
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, 'ucs2'));
var hmac = crypto.createHmac("md5", ntlmhash);
hmac.update(new Buffer.from(username.toUpperCase() + authTargetName, "ucs2"));
return hmac.digest();
}
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
type2message.challenge.copy(buf, 8);
//client nonce
buf.write(nonce || createPseudoRandomValue(16), 16, 'hex');
buf.write(nonce || createPseudoRandomValue(16), 16, "hex");
//create hash
hmac.update(buf.slice(8));
var hashedBuffer = hmac.digest();
@@ -73,7 +76,9 @@ function createLMv2Response(type2message, username, ntlmhash, nonce, targetName)
return buf;
}
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
//server challenge
type2message.challenge.copy(buf, 8);
@@ -86,12 +91,12 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
// maybe think about a different solution here
// 11644473600000 = diff between 1970 and 1601
var timestamp = ((Date.now() + 11644473600000) * 10000).toString(16);
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 timestampLow = Number("0x" + timestamp.substring(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(timestampHigh, 28, false);
//random client nonce
buf.write(nonce || createPseudoRandomValue(16), 32, 'hex');
buf.write(nonce || createPseudoRandomValue(16), 32, "hex");
//zero
buf.writeUInt32LE(0, 40);
//complete target information block from type 2 message
@@ -104,7 +109,7 @@ function createNTLMv2Response(type2message, username, ntlmhash, nonce, targetNam
return buf;
}
function createPseudoRandomValue(length) {
var str = '';
var str = "";
while (str.length < length) {
str += crypto.randomInt(16).toString(16);
}
@@ -117,6 +122,6 @@ module.exports = {
createNTLMResponse: createNTLMResponse,
createLMv2Response: createLMv2Response,
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
var os = require('os'), flags = require('./flags'), hash = require('./hash');
var os = require("os"),
flags = require("./flags"),
hash = require("./hash");
var NTLMSIGNATURE = "NTLMSSP\0";
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;
target = target === undefined ? '' : target;
target = target === undefined ? "" : target;
//signature
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, 'ascii');
buf.write(NTLMSIGNATURE, pos, NTLMSIGNATURE.length, "ascii");
pos += NTLMSIGNATURE.length;
//message type
buf.writeUInt32LE(1, pos);
pos += 4;
//flags
buf.writeUInt32LE(flags.NTLMFLAG_NEGOTIATE_OEM |
flags.NTLMFLAG_REQUEST_TARGET |
flags.NTLMFLAG_NEGOTIATE_NTLM_KEY |
flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY |
flags.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN, pos);
buf.writeUInt32LE(
flags.NTLMFLAG_NEGOTIATE_OEM |
flags.NTLMFLAG_REQUEST_TARGET |
flags.NTLMFLAG_NEGOTIATE_NTLM_KEY |
flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY |
flags.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN,
pos
);
pos += 4;
//domain security buffer
buf.writeUInt16LE(target.length, pos);
@@ -27,7 +34,7 @@ function createType1Message(workstation, target) {
buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos);
pos += 4;
if (target.length > 0) {
dataPos += buf.write(target, dataPos, 'ascii');
dataPos += buf.write(target, dataPos, "ascii");
}
//workstation security buffer
buf.writeUInt16LE(workstation.length, pos);
@@ -37,40 +44,40 @@ function createType1Message(workstation, target) {
buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos);
pos += 4;
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) {
if (str === undefined) {
throw new Error('Invalid argument');
throw new Error("Invalid argument");
}
//convenience
if (Object.prototype.toString.call(str) !== '[object String]') {
if (str.hasOwnProperty('headers') && str.headers.hasOwnProperty('www-authenticate')) {
str = str.headers['www-authenticate'];
}
else {
throw new Error('Invalid argument');
if (Object.prototype.toString.call(str) !== "[object String]") {
if (str.hasOwnProperty("headers") && str.headers.hasOwnProperty("www-authenticate")) {
str = str.headers["www-authenticate"];
} else {
throw new Error("Invalid argument");
}
}
var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str);
if (ntlmMatch) {
str = ntlmMatch[1];
}
var buf = new Buffer.from(str, 'base64'), obj = {};
var buf = new Buffer.from(str, "base64"),
obj = {};
//check signature
if (buf.toString('ascii', 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
throw new Error('Invalid message signature: ' + str);
if (buf.toString("ascii", 0, NTLMSIGNATURE.length) !== NTLMSIGNATURE) {
throw new Error("Invalid message signature: " + str);
}
//check message type
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
obj.flags = buf.readUInt32LE(20);
obj.encoding = (obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM) ? 'ascii' : 'ucs2';
obj.version = (obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY) ? 2 : 1;
obj.encoding = obj.flags & flags.NTLMFLAG_NEGOTIATE_OEM ? "ascii" : "ucs2";
obj.version = obj.flags & flags.NTLMFLAG_NEGOTIATE_NTLM2_KEY ? 2 : 1;
obj.challenge = buf.slice(24, 32);
//read target name
obj.targetName = (function () {
@@ -78,10 +85,10 @@ function decodeType2Message(str) {
//skipping allocated space
var offset = buf.readUInt32LE(16);
if (length === 0) {
return '';
return "";
}
if ((offset + length) > buf.length || offset < 32) {
throw new Error('Bad type 2 message');
if (offset + length > buf.length || offset < 32) {
throw new Error("Bad type 2 message");
}
return buf.toString(obj.encoding, offset, offset + length);
})();
@@ -97,11 +104,11 @@ function decodeType2Message(str) {
if (length === 0) {
return info;
}
if ((offset + length) > buf.length || offset < 32) {
throw new Error('Bad type 2 message');
if (offset + length > buf.length || offset < 32) {
throw new Error("Bad type 2 message");
}
var pos = offset;
while (pos < (offset + length)) {
while (pos < offset + length) {
var blockType = buf.readUInt16LE(pos);
pos += 2;
var blockLength = buf.readUInt16LE(pos);
@@ -113,39 +120,40 @@ function decodeType2Message(str) {
var blockTypeStr = void 0;
switch (blockType) {
case 1:
blockTypeStr = 'SERVER';
blockTypeStr = "SERVER";
break;
case 2:
blockTypeStr = 'DOMAIN';
blockTypeStr = "DOMAIN";
break;
case 3:
blockTypeStr = 'FQDN';
blockTypeStr = "FQDN";
break;
case 4:
blockTypeStr = 'DNS';
blockTypeStr = "DNS";
break;
case 5:
blockTypeStr = 'PARENT_DNS';
blockTypeStr = "PARENT_DNS";
break;
default:
blockTypeStr = '';
blockTypeStr = "";
break;
}
if (blockTypeStr) {
info[blockTypeStr] = buf.toString('ucs2', pos, pos + blockLength);
info[blockTypeStr] = buf.toString("ucs2", pos, pos + blockLength);
}
pos += blockLength;
}
return {
parsed: info,
buffer: targetInfoBuffer
buffer: targetInfoBuffer,
};
})();
}
return obj;
}
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) {
workstation = os.hostname();
}
@@ -153,12 +161,15 @@ function createType3Message(type2Message, username, password, workstation, targe
target = type2Message.targetName;
}
//signature
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, 'ascii');
buf.write(NTLMSIGNATURE, 0, NTLMSIGNATURE.length, "ascii");
//message type
buf.writeUInt32LE(3, 8);
if (type2Message.version === 2) {
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
buf.writeUInt16LE(lmv2.length, 12);
buf.writeUInt16LE(lmv2.length, 14);
@@ -171,9 +182,11 @@ function createType3Message(type2Message, username, password, workstation, targe
buf.writeUInt32LE(dataPos, 24);
ntlmv2.copy(buf, dataPos);
dataPos += ntlmv2.length;
}
else {
var lmHash = hash.createLMHash(password), ntlmHash = hash.createNTLMHash(password), lm = hash.createLMResponse(type2Message.challenge, lmHash), ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
} else {
var lmHash = hash.createLMHash(password),
ntlmHash = hash.createNTLMHash(password),
lm = hash.createLMResponse(type2Message.challenge, lmHash),
ntlm = hash.createNTLMResponse(type2Message.challenge, ntlmHash);
//lm security buffer
buf.writeUInt16LE(lm.length, 12);
buf.writeUInt16LE(lm.length, 14);
@@ -188,18 +201,18 @@ function createType3Message(type2Message, username, password, workstation, targe
dataPos += ntlm.length;
}
//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, 30);
buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 28);
buf.writeUInt16LE(type2Message.encoding === "ascii" ? target.length : target.length * 2, 30);
buf.writeUInt32LE(dataPos, 32);
dataPos += buf.write(target, dataPos, type2Message.encoding);
//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, 38);
buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 36);
buf.writeUInt16LE(type2Message.encoding === "ascii" ? username.length : username.length * 2, 38);
buf.writeUInt32LE(dataPos, 40);
dataPos += buf.write(username, dataPos, type2Message.encoding);
//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, 46);
buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 44);
buf.writeUInt16LE(type2Message.encoding === "ascii" ? workstation.length : workstation.length * 2, 46);
buf.writeUInt32LE(dataPos, 48);
dataPos += buf.write(workstation, dataPos, type2Message.encoding);
if (type2Message.version === 2) {
@@ -210,11 +223,11 @@ function createType3Message(type2Message, username, password, workstation, targe
//flags
buf.writeUInt32LE(type2Message.flags, 60);
}
return 'NTLM ' + buf.toString('base64', 0, dataPos);
return "NTLM " + buf.toString("base64", 0, dataPos);
}
module.exports = {
createType1Message: createType1Message,
decodeType2Message: decodeType2Message,
createType3Message: createType3Message
createType3Message: createType3Message,
};
//# sourceMappingURL=ntlm.js.map
//# sourceMappingURL=ntlm.js.map

View File

@@ -1,62 +1,176 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
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;
var __createBinding =
(this && this.__createBinding) ||
(Object.create
? function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, {
enumerable: true,
get: function () {
return m[k];
},
});
}
: function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
});
var __setModuleDefault =
(this && this.__setModuleDefault) ||
(Object.create
? function (o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}
: function (o, v) {
o["default"] = v;
});
var __importStar =
(this && this.__importStar) ||
function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter =
(this && this.__awaiter) ||
function (thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P
? value
: new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
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 };
};
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
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 });
exports.NtlmClient = void 0;
var axios_1 = __importDefault(require("axios"));
@@ -65,11 +179,11 @@ var https = __importStar(require("https"));
var http = __importStar(require("http"));
var dev_null_1 = __importDefault(require("dev-null"));
/**
* @param credentials An NtlmCredentials object containing the username and password
* @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
*/
* @param credentials An NtlmCredentials object containing the username and password
* @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
*/
function NtlmClient(credentials, AxiosConfig) {
var _this = this;
var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {};
@@ -80,51 +194,76 @@ function NtlmClient(credentials, AxiosConfig) {
config.httpsAgent = new https.Agent({ keepAlive: true });
}
var client = axios_1.default.create(config);
client.interceptors.response.use(function (response) {
return response;
}, function (err) { return __awaiter(_this, void 0, void 0, function () {
var error, t1Msg, t2Msg, t3Msg, stream_1;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
error = err.response;
// The header may look like this: `Negotiate, NTLM, Basic realm="itsahiddenrealm.example.net"`Add commentMore actions
// so extract the 'NTLM' part first
const ntlmheader = error.headers['www-authenticate'].split(',').find(_ => _.match(/ *NTLM/))?.trim() || '';
if (!(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;
client.interceptors.response.use(
function (response) {
return response;
},
function (err) {
return __awaiter(_this, void 0, void 0, function () {
var error, t1Msg, t2Msg, t3Msg, stream_1;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
error = err.response;
// The header may look like this: `Negotiate, NTLM, Basic realm="itsahiddenrealm.example.net"`Add commentMore actions
// so extract the 'NTLM' part first
const ntlmheader =
error.headers["www-authenticate"]
.split(",")
.find((_) => _.match(/ *NTLM/))
?.trim() || "";
if (
!(
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;
}
exports.NtlmClient = NtlmClient;
//# sourceMappingURL=ntlmClient.js.map
//# sourceMappingURL=ntlmClient.js.map

View File

@@ -1,20 +1,20 @@
import { PluginFunc, ConfigType } from 'dayjs'
import { PluginFunc, ConfigType } from "dayjs";
declare const plugin: PluginFunc
export = plugin
declare const plugin: PluginFunc;
export = plugin;
declare module 'dayjs' {
interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs
offsetName(type?: 'short' | 'long'): string | undefined
}
declare module "dayjs" {
interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs;
offsetName(type?: "short" | "long"): string | undefined;
}
interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs
(date: ConfigType, format: string, timezone?: string): Dayjs
guess(): string
setDefault(timezone?: string): void
}
interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs;
(date: ConfigType, format: string, timezone?: string): Dayjs;
guess(): string;
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
* License: MIT
*/
!function (t, e) {
!(function (t, e) {
// 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();
}(this, (function () {
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());
})(this, function () {
"use strict";
let t = {
year: 0,
@@ -15,7 +19,7 @@
day: 2,
hour: 3,
minute: 4,
second: 5
second: 5,
};
let e = {};
return function (n, i, o) {
@@ -23,23 +27,28 @@
let a = function (t, n, i) {
void 0 === i && (i = {});
let o = new Date(t);
let r = function (t, n) {
let r = (function (t, n) {
void 0 === n && (n = {});
let i = n.timeZoneName || "short";
let o = t + "|" + i;
let r = e[o];
return r || (r = new Intl.DateTimeFormat("en-US", {
hour12: !1,
timeZone: t,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: i
}), e[o] = r), r;
}(n, i);
return (
r ||
((r = new Intl.DateTimeFormat("en-US", {
hour12: !1,
timeZone: t,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: i,
})),
(e[o] = r)),
r
);
})(n, i);
return r.formatToParts(o);
};
let u = function (e, n) {
@@ -60,56 +69,62 @@
return (o.utc(v).valueOf() - (h -= h % 1e3)) / 6e4;
};
let f = i.prototype;
f.tz = function (t, e) {
((f.tz = function (t, e) {
void 0 === t && (t = r);
let n = this.utcOffset();
let i = this.toDate();
let a = i.toLocaleString("en-US", { timeZone: t }).replace("\u202f", " ");
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) {
let s = f.utcOffset();
f = f.add(n - s, "minute");
}
return f.$x.$timezone = t, f;
}, f.offsetName = function (t) {
let e = this.$x.$timezone || o.tz.guess();
let n = a(this.valueOf(), e, { timeZoneName: t }).find((function (t) {
return t.type.toLowerCase() === "timezonename";
return ((f.$x.$timezone = t), f);
}),
(f.offsetName = function (t) {
let e = this.$x.$timezone || o.tz.guess();
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;
f.startOf = function (t, e) {
((f.startOf = function (t, e) {
if (!this.$x || !this.$x.$timezone) {
return s.call(this, t, e);
}
let n = o(this.format("YYYY-MM-DD HH:mm:ss:SSS"));
return s.call(n, t, e).tz(this.$x.$timezone, !0);
}, o.tz = function (t, e, n) {
let i = n && e;
let a = n || e || r;
let f = u(+o(), 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 ];
}),
(o.tz = function (t, e, n) {
let i = n && e;
let a = n || e || r;
let f = u(+o(), a);
if (typeof t != "string") {
return o(t).tz(a);
}
let r = u(i -= 60 * (o - e) * 1e3, n);
return o === r ? [ i, o ] : [ t - 60 * Math.min(o, r) * 1e3, Math.max(o, r) ];
}(o.utc(t, i).valueOf(), f, a);
let m = s[0];
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;
};
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);
return o === r ? [i, o] : [t - 60 * Math.min(o, r) * 1e3, Math.max(o, r)];
})(o.utc(t, i).valueOf(), f, a);
let m = s[0];
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